Dynomotion

Group: DynoMotion Message: 9879 From: engnerdan Date: 7/29/2014
Subject: Expanding on the Modbus RTU Example

There was a discussion on here a while back initiated by Moray about adding some additional modbus addresses/functions to the modbus code example. I am looking to do the same thing but I am incredibly lost looking at this example program (no C experience before). If Moray or someone else could share their code and maybe help walk me through what is happening in the code that would be great. 


-Dan

dan@...

Group: DynoMotion Message: 9880 From: Moray Cuthill Date: 7/29/2014
Subject: Re: Expanding on the Modbus RTU Example
Hi,
 
I just had to dig out a backup of my main init file to figure out how I eventually implemented things. It is some quite complicated code, as there is quite a bit of error checking/handling involved, and I can't remember exactly everything that I had to change to get it to work.
I've copied in the relevant code from my init file, so you can compare it to the supplied modbus master example to see where I've added bits (I'd go through it and check, but I've not got the time as I'm currently in the middle of a new machine build!)
 
For my application, I have a Click PLC with an additional IO module to control my toolchanger, and provide some additional inputs. All I'm doing via modbus, is read the current tool position from a register, write the desired position to a register, and read the current status of the inputs on the additional IO, so I'm only dealing with 3 registers.
 
From a quick scan through, I use RegUnload() to read the additional input register to test for active bits which correspond to the inputs.
RegLoad copies the desired tool position into a register (*TCDesired and *TCPosition are pointers to user data locations which are used for transfer to/from Mach)
RegTransfer is used to read the current tool position from a register to the relevant userdata.
 
What registers are actually read/written, is setup in ModbusMaster_Cmds ModbusMaster_MonitorList[]
Each line translates to a single MBregisters[] which is then used in the RegLoad/Unload functions, which explains why I'm referencing specific array indexes within those functions.
The key options/commands (cmd) are 3 - read input register, 4 read register (as in memory location), 10 write register (this can be any writeable register i.e. memory or output).
 
I think that's all the basics covered. I did add a good bit debug code when doing this, so if you think there's something there that's not needed, then that's most likely why!
 
Here's the code-
 
// Start MODbus
#include "RS232/ModBus/ModBusMaster.h"
#define TCPOS 5
#define TCDES 6

unsigned short MBRegisters[N_MB_REGISTERS];
double delayT = 0;
double delayT2 = 0;
// Constants
int ModbusMaster_MaxRetry=3;
double ModbusMaster_Timeout=0.5; //seconds for no response for a send command
double ModbusMaster_ResponseTime; //seconds for last PLC response
double ModbusMaster_CommandSentTime;
// status and performance counters
double ModbusMaster_MonitorStartTime=0; // start of most recent monitor cycle
double ModbusMaster_MonitorCycleTime=0; // seconds to call all commands in Monitor list
int ModbusMaster_TallyConnections=0; // Number of times Connection list has been sent
int ModbusMaster_TallyCommands=0; // Commands since connection
int ModbusMaster_TallyRetries=0; // Retries since connection
// statuses, counters, etc
int ModbusMaster_List=0; // 0=connect, 1=monitor
int ModbusMaster_Connected=0;
int ModbusMaster_MonitorIndex=0;
int ModbusMaster_ConnectIndex=0;
int ModbusMaster_Idle=0; // 0=idle, 1=await reply
int ModbusMaster_Retry=0;
double ModbusMaster_LastInTime=0;
double ModbusMaster_EndOfPacketWait=3.5*10/9600; // wait 3.5 characters after a packet 9600=baud rate)
char ModbusMaster_packetBuild[256]; // max length of modbus frame
int ModbusMaster_packetSize=0;
double *TCPosition  = (double *)&persist.UserData[(TCPOS -1)*2];
double *TCDesired = (double *)&persist.UserData[(TCDES-1)*2];
   
typedef enum
{
 MBERROR_NONE = 0,
 // Modbus codes; reported with Modbus error packet
 MBERROR_ILLEGAL_FUNCTION = 1,
 MBERROR_ILLEGAL_DATA_ADDRESS = 2, // used
 MBERROR_ILLEGAL_DATA_VALUE = 3, // used
 MBERROR_SLAVE_DEVICE_FAILURE = 4,
 MBERROR_ACKNOWLEDGE = 5,
 MBERROR_SLAVE_DEVICE_BUSY = 6,
 MBERROR_NEGATIVE_ACKNOWLEDGE = 7,
 MBERROR_MEMORY_PARITY_ERROR = 8,
 // internal codes
 INTERROR_WRONG_DEVICE = 9,
 INTERROR_CHECKSUM = 10,
 INTERROR_TIMEOUT = 11,
} MBErrors;

typedef struct ModbusMaster_sCmds
{
 char *start; // has "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. Not data and Not checksum.
 int len; // length of start string. commonly 6
 int reg; // reg# is the start index into the MBRegisters array for the command
} ModbusMaster_Cmds;
ModbusMaster_Cmds *ModbusMaster_SentPtr; // pointer to current command. used to send, resend, interpret response.
ModbusMaster_Cmds ModbusMaster_ConnectList[] =
{
 // string is "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. bytelen, data, and checksum are added.
 {"\x01\x04\xF0\x00\x00\x10", 6, 2}, // Collect PLC firmware info block MBRegisters[10] for 16 registers
 {0,0,0} // end flag
};
ModbusMaster_Cmds ModbusMaster_MonitorList[] =
{
 // string is "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. bytelen, data, and checksum are added as necessary.
 {"\x01\x04\xE0\x00\x00\x01", 6, 0}, // Read inputs to MBRegisters[0]
 {"\x01\x10\xE2\x00\x00\x01", 6, 1}, // Write outputs from MBRegisters[1]
 {"\x01\x03\x00\x00\x00\x01", 6, 2}, // Read DS1
 {"\x01\x10\x00\x01\x00\x01", 6, 3}, // Write DS2
 {0,0,0} // end flag
};
void ModbusMaster_Init()
{
 printf("\nModbus Master Init\n");
 EnableRS232Cmds(RS232_BAUD_38400);
 DoRS232Cmds = FALSE;  // turn off processing RS232 input as commands
 ModbusMaster_LastInTime=Time_sec();
 ModbusMaster_EndOfPacketWait=3.5*10.0/38400; // wait 3.5 characters after a packet
 ModbusMaster_packetSize=0;
 
 ModbusMaster_Idle=0;
 ModbusMaster_SentPtr=&ModbusMaster_ConnectList[0];
 int c;
 for (c=0;c<N_MB_REGISTERS;c++)
  MBRegisters[c]=0;
  
 // make the register static arrays available to the other threads
 persist.UserData[PERSIST_MBREG_BLOCK_ADR]=(int)MBRegisters;
 //d printf("persist.UserData[%d]<=%08X\n",PERSIST_RWREG_BLOCK_ADR,MBRWRegisters); //debug
}
char* strncpy(char *dst,char* src,int len)
{
 int i;
 for (i=0;i<len;i++)
  dst[i]=src[i];
 return dst;
}
// marshal and move values read from PLC/Slave into MBRegisters to KFlop memory
void ModbusMaster_RegUnload()
{
 // Move 8 PLC inputs to virtual bits via MBRegisters[0]
 // Note use SetStateBit which is Atomic and Thread Safe
 
 int i;
 if(delayT2 < Time_sec()) // anything in here only gets handled once every tDelay seconds
  {
 delayT2 = Time_sec()+2;
  //printf("Input dump%d\n", MBRegisters[0]);
 if(MBRegisters[0] & 32){
  //printf("&32Door Closed\n");
 }
 if(MBRegisters[0] & 64){
  //printf("&64Lube Low\n");
 }
}
 //for (i=0; i<8; i++)
 // SetStateBit(48+i,(MBRegisters[0]>>i)&1);  // 8 input bits
}
// marshal and move values to be sent to PLC/Slave into MBRegisters
void ModbusMaster_RegLoad()
{
 // Move 6 virtual bits to PLC outputs via MBRegisters[1]
 MBRegisters[1] = (VirtualBits>>8)&0x3F; // the six bits after the 8 input bits
 int tcd = *TCDesired;
 MBRegisters[3] = *TCDesired;
 if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
 {
  printf("Desired-%d\n",tcd);
  delayT = Time_sec()+2;
 }
}
void ModbusMaster_RegTransfer()
{
 int tc = MBRegisters[2];
 *TCPosition = tc;
 if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
 {
  //printf("CLICK Register-%d\n",tc);
  //delayT = Time_sec()+2;
 }
}
void ModbusMaster_RegWrite()
{
 //MBRegisters[3] = persist.UserData[12];
 MBRegisters[3] = 0x06;
}
static unsigned char auchCRCHi[] = {
 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
 0x40
} ;
static char auchCRCLo[] = {
 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
 0x40
} ;

unsigned short CRC16(unsigned char *puchMsg,unsigned short usDataLen)
{
 unsigned char uchCRCHi = 0xff;
 unsigned char uchCRCLo = 0xff;
 unsigned int uIndex;
 while(usDataLen--)
 {
  uIndex = uchCRCLo ^ *puchMsg++;
  uchCRCLo = uchCRCHi ^ auchCRCHi[uIndex];
  uchCRCHi = auchCRCLo[uIndex];
 }
 return (uchCRCHi<<8|uchCRCLo);
}
void ModbusMaster_NextCmd(MBErrors ecode)
{
 if (ecode) printf("ModbusMaster_NextCmd(%d)\n",ecode); //debug
 ModbusMaster_Idle=0; // ready to send a new command
 if (INTERROR_TIMEOUT==ecode)
 {
  if (ModbusMaster_List)
   ModbusMaster_Connected=0;
 }
 if (ModbusMaster_List)
 {
  ModbusMaster_MonitorIndex++;
  if (!ModbusMaster_MonitorList[ModbusMaster_MonitorIndex].start)
  {
   ModbusMaster_MonitorIndex=0;
   ModbusMaster_MonitorCycleTime=Time_sec()-ModbusMaster_MonitorStartTime;
   ModbusMaster_MonitorStartTime=Time_sec();
  }
 }
 else
 {
  ModbusMaster_ConnectIndex++;
  if (!ModbusMaster_ConnectList[ModbusMaster_ConnectIndex].start)
   ModbusMaster_List=1; // continue monitor list, do not restart here
 }
 if (INTERROR_TIMEOUT!=ecode&&0==ModbusMaster_Connected)
 {
  ModbusMaster_Connected=1;
  ModbusMaster_List=0;
  ModbusMaster_ConnectIndex=0;
  ModbusMaster_TallyConnections++;
  ModbusMaster_TallyCommands=0;
  ModbusMaster_TallyRetries=0;
 }
 if (!ModbusMaster_List)
  ModbusMaster_SentPtr=&ModbusMaster_ConnectList[ModbusMaster_ConnectIndex];
 else
  ModbusMaster_SentPtr=&ModbusMaster_MonitorList[ModbusMaster_MonitorIndex];
}
void ModbusMaster_Send(int verbose)
{
 // send the command currently pointed to by ModbusMaster_SentPtr
 // printf("ModbusMaster_Send(%d)\n",verbose);
 
 char *chp;
 int x;
 unsigned char *xp;
 if (!ModbusMaster_List)
  printf("List:%d ConnectIndex:%d MonitorIndex:%d\n",
    ModbusMaster_List,ModbusMaster_ConnectIndex,ModbusMaster_MonitorIndex);
 if (!ModbusMaster_SentPtr->start)
 {
  printf("ModbusMaster_Send: Tried to execute at end list\n");
  ModbusMaster_NextCmd(MBERROR_NONE);
 }
  
 strncpy(ModbusMaster_packetBuild,ModbusMaster_SentPtr->start,ModbusMaster_SentPtr->len);
 chp=&ModbusMaster_packetBuild[ModbusMaster_SentPtr->len];
 switch(ModbusMaster_packetBuild[1])
 {
  case 0x10: // RW Write
   ModbusMaster_RegLoad();
   *chp++=ModbusMaster_packetBuild[5]*2;
   for (x=0;x<ModbusMaster_packetBuild[5];x++)
   {
    *chp++=(MBRegisters[x+ModbusMaster_SentPtr->reg]>>8)&0xFF;
    *chp++=MBRegisters[x+ModbusMaster_SentPtr->reg]&0xFF;
   }
   break;
  case 0x02: // Bit Read
  case 0x03: // RO Read
  case 0x04: // RW Read
   break;
  default:
   printf("Unexpected default: ModbusMaster_Send(), Function %d\n",ModbusMaster_packetBuild[1]); //debug
   break;
 }
 int csum=CRC16(ModbusMaster_packetBuild,chp-ModbusMaster_packetBuild);
 *chp++=csum&0xFF;
 *chp++=(csum>>8)&0xFF;
 if (verbose) printf("Tx:"); //debug
 for (xp=ModbusMaster_packetBuild;xp<chp;xp++)
 {
  RS232_PutChar(*xp);
  if (verbose) printf("%02x;",*xp); //debug
 }
 if (verbose) printf("\n"); //debug
 
 ModbusMaster_LastInTime=Time_sec();
 ModbusMaster_Idle=1;
}
MBErrors Process_Data(unsigned char *Buffer, unsigned char Count)
{
 int regndx;
 int cnt;
 int x;
 unsigned short CRC = (((Buffer[Count-1]<<8)&0xFF00)|(Buffer[Count-2]&0xFF)); // Received CRC
 unsigned short Recalculated_CRC = CRC16(Buffer,Count-2); // Computed CRC
 if(Recalculated_CRC != CRC)
 {
  printf("Count %d\n",Count);
  printf("Checksum: Theirs:%04X Mine:%04X, %d chars\n",CRC,Recalculated_CRC,Count); //debug
  return INTERROR_CHECKSUM;
 }
 //d printf("Packet %d\n",Function);    //debug
 switch(Buffer[1])
 {
  case 0x02:
   //ModbusMaster_BitUnload();
   break;
  case 0x03:
   //printf((int)Buffer[3]);
   //printf("Buffers-",Buffer[2]);
   cnt=Buffer[2];
   //printf("Buffers-%d\n",cnt);
   //MBRegisters[2]=Buffer[4];
   MBRegisters[2]=((Buffer[3]<<8)&0xFF00)|(Buffer[4]&0x00FF);
   //printf("Register-%d\n",MBRegisters[2]);
   ModbusMaster_RegTransfer();
   break;
  case 0x04:
   regndx=ModbusMaster_SentPtr->reg;
   cnt=Buffer[2];
   //printf("Buffers-%d\n",cnt);
   //printf(cnt);
   for (x=0;x<cnt;x+=2)
    MBRegisters[regndx++]=((Buffer[3+x]<<8)&0xFF00)|(Buffer[4+x]&0x00FF);
   //printf(MBRegisters[1]);
   ModbusMaster_RegUnload();
   break;
  case 0x10:
   // no action on successful write
   break;
  default:
   printf("Unexpected default: Process_Data(), Buffer[1]=%d\n",Buffer[1]);
   break;
 }
 
 return MBERROR_NONE; //We made it to the end, return
}
 
void ModbusMaster_Monitor()
{
 char c;
 if (pRS232RecIn != pRS232RecOut)
 {
  ModbusMaster_LastInTime=Time_sec();
  while (pRS232RecIn != pRS232RecOut) // data in buffer
  {
   c=RS232_GetChar();
   if (ModbusMaster_packetSize<255)
    ModbusMaster_packetBuild[ModbusMaster_packetSize++]=c;
   //d printf("%02x,",c&0xFF); //debug
  }
 }
 else
 {
  if (ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait<Time_sec() && ModbusMaster_packetSize)
  {
   int rtrn=Process_Data(ModbusMaster_packetBuild,ModbusMaster_packetSize);
   ModbusMaster_packetSize=0; // ready for next packet
   if (!rtrn)
   {
    ModbusMaster_TallyCommands++;
    ModbusMaster_NextCmd(rtrn);
    return;
   }
   printf("Error=%d, %f\n",rtrn,ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait-Time_sec()); //debug
   //d printf("\n",c); //debug
  }
  if (ModbusMaster_LastInTime+ModbusMaster_Timeout<Time_sec()) // retry test
  {
   ModbusMaster_Retry++;
   ModbusMaster_TallyRetries++;
   printf("ModbusMaster_Retry:%d\n",ModbusMaster_Retry); //debug
   if (ModbusMaster_Retry>ModbusMaster_MaxRetry)
   {
    //d printf("Failed Monitor message %d\n",ModbusMaster_MonitorIndex); //debug
    //ModbusMaster_ConnectIndex=0; // reset connection
    ModbusMaster_NextCmd(INTERROR_TIMEOUT);
   }
   else
   {
    ModbusMaster_Send(1);
   }
  }
 }
}

void ModbusMaster_Loop()
{
 //d printf("ModbusMaster_Loop: ModbusMaster_Idle=%d\n",ModbusMaster_Idle); //debug
 if(!ModbusMaster_Idle)
 {
  int x;
  //if (ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait>Time_sec())
  // ;
  //printf("ModbusMaster_Loop: ModbusMaster_Idle=%d\n",ModbusMaster_Idle); //debug
  ModbusMaster_Send(0);
  //for (x=0;x<16;x++)
  //{
  // printf("%04d ",(unsigned)MBRegisters[x]);
  // if ((x&7)==7) printf("\n");
  //}
  ModbusMaster_Retry=0;
 }
 else
  ModbusMaster_Monitor();
}
// End MODbus
 
int main()
{
 ModbusMaster_Init();
 int reportsecs=10;
 double starttime;;
 double MonitorStartTime=0; // start of most recent monitor cycle
 double MonitorCycleTime=0; // seconds to call all commands in Monitor list
 int TallyConnections=0; // Number of times Connecion list has been sent
 int TallyCommands=0; // Commands since connection
 int TallyRetries=0; // Retries since connection
 starttime=Time_sec();
 TallyCommands=ModbusMaster_TallyCommands;
 for(;;) // loop forever
 {
  ModbusMaster_Loop();
  if (starttime+reportsecs<Time_sec())
  {
   printf("\nSeconds: %d\n",reportsecs);
   printf("ModbusMaster_MonitorCycleTime=%f (%f/s)\n",ModbusMaster_MonitorCycleTime,1.0/ModbusMaster_MonitorCycleTime);
   printf("ModbusMaster_TallyCommands/s=%0.lf\n",(ModbusMaster_TallyCommands-TallyCommands)/(double)reportsecs);
   printf("ModbusMaster_TallyConnections=%d\n",ModbusMaster_TallyConnections);
   printf("ModbusMaster_TallyRetries=%d\n",ModbusMaster_TallyRetries);
 
   starttime=Time_sec();
   TallyCommands=ModbusMaster_TallyCommands;
  }
 } // end of forever
    return 0;
}
 
Moray
  
  if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
  {
   //printf("tick\n");
   delayT = Time_sec()+tDelay;
  }
 
 
 
On Tue, Jul 29, 2014 at 9:09 PM, engnerdan@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
 

There was a discussion on here a while back initiated by Moray about adding some additional modbus addresses/functions to the modbus code example. I am looking to do the same thing but I am incredibly lost looking at this example program (no C experience before). If Moray or someone else could share their code and maybe help walk me through what is happening in the code that would be great. 


-Dan

dan@...


Group: DynoMotion Message: 9888 From: Dan Date: 7/31/2014
Subject: Re: Expanding on the Modbus RTU Example
Moray,
Thanks I will take a look at the code later. What I am trying to do is send the ADC value from 4 of the Kanalog inputs to the PLC and then have an HMI connected to the PLC display those values as moving bar graphs. These are to show the load on each of my servos and my spindle. I was going to try and modify the KmotionCNC GUI to add the bar graph load meters, but I have too much to learn with C that I don’t need to try working on C++ and the GUI too.
 
Additionally I think it would require that I rewrite the GUI code each time there is a version update. The additional digital I/O of the PLC will not hurt any either, I am out of I/O on my Kanalog and have not even started designing my tool changer.
 
 
Dan
 
Sent: Tuesday, July 29, 2014 4:42 PM
Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
 
 

Hi,
 
I just had to dig out a backup of my main init file to figure out how I eventually implemented things. It is some quite complicated code, as there is quite a bit of error checking/handling involved, and I can't remember exactly everything that I had to change to get it to work.
I've copied in the relevant code from my init file, so you can compare it to the supplied modbus master example to see where I've added bits (I'd go through it and check, but I've not got the time as I'm currently in the middle of a new machine build!)
 
For my application, I have a Click PLC with an additional IO module to control my toolchanger, and provide some additional inputs. All I'm doing via modbus, is read the current tool position from a register, write the desired position to a register, and read the current status of the inputs on the additional IO, so I'm only dealing with 3 registers.
 
From a quick scan through, I use RegUnload() to read the additional input register to test for active bits which correspond to the inputs.
RegLoad copies the desired tool position into a register (*TCDesired and *TCPosition are pointers to user data locations which are used for transfer to/from Mach)
RegTransfer is used to read the current tool position from a register to the relevant userdata.
 
What registers are actually read/written, is setup in ModbusMaster_Cmds ModbusMaster_MonitorList[]
Each line translates to a single MBregisters[] which is then used in the RegLoad/Unload functions, which explains why I'm referencing specific array indexes within those functions.
The key options/commands (cmd) are 3 - read input register, 4 read register (as in memory location), 10 write register (this can be any writeable register i.e. memory or output).
 
I think that's all the basics covered. I did add a good bit debug code when doing this, so if you think there's something there that's not needed, then that's most likely why!
 
Here's the code-
 
// Start MODbus
#include "RS232/ModBus/ModBusMaster.h"
#define TCPOS 5
#define TCDES 6

unsigned short MBRegisters[N_MB_REGISTERS];
double delayT = 0;
double delayT2 = 0;
// Constants
int ModbusMaster_MaxRetry=3;
double ModbusMaster_Timeout=0.5; //seconds for no response for a send command
double ModbusMaster_ResponseTime; //seconds for last PLC response
double ModbusMaster_CommandSentTime;
// status and performance counters
double ModbusMaster_MonitorStartTime=0; // start of most recent monitor cycle
double ModbusMaster_MonitorCycleTime=0; // seconds to call all commands in Monitor list
int ModbusMaster_TallyConnections=0; // Number of times Connection list has been sent
int ModbusMaster_TallyCommands=0; // Commands since connection
int ModbusMaster_TallyRetries=0; // Retries since connection
// statuses, counters, etc
int ModbusMaster_List=0; // 0=connect, 1=monitor
int ModbusMaster_Connected=0;
int ModbusMaster_MonitorIndex=0;
int ModbusMaster_ConnectIndex=0;
int ModbusMaster_Idle=0; // 0=idle, 1=await reply
int ModbusMaster_Retry=0;
double ModbusMaster_LastInTime=0;
double ModbusMaster_EndOfPacketWait=3.5*10/9600; // wait 3.5 characters after a packet 9600=baud rate)
char ModbusMaster_packetBuild[256]; // max length of modbus frame
int ModbusMaster_packetSize=0;
double *TCPosition  = (double *)&persist.UserData[(TCPOS -1)*2];
double *TCDesired = (double *)&persist.UserData[(TCDES-1)*2];
   
typedef enum
{
MBERROR_NONE = 0,
// Modbus codes; reported with Modbus error packet
MBERROR_ILLEGAL_FUNCTION = 1,
MBERROR_ILLEGAL_DATA_ADDRESS = 2, // used
MBERROR_ILLEGAL_DATA_VALUE = 3, // used
MBERROR_SLAVE_DEVICE_FAILURE = 4,
MBERROR_ACKNOWLEDGE = 5,
MBERROR_SLAVE_DEVICE_BUSY = 6,
MBERROR_NEGATIVE_ACKNOWLEDGE = 7,
MBERROR_MEMORY_PARITY_ERROR = 8,
// internal codes
INTERROR_WRONG_DEVICE = 9,
INTERROR_CHECKSUM = 10,
INTERROR_TIMEOUT = 11,
} MBErrors;

typedef struct ModbusMaster_sCmds
{
char *start; // has "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. Not data and Not checksum.
int len; // length of start string. commonly 6
int reg; // reg# is the start index into the MBRegisters array for the command
} ModbusMaster_Cmds;
ModbusMaster_Cmds *ModbusMaster_SentPtr; // pointer to current command. used to send, resend, interpret response.
ModbusMaster_Cmds ModbusMaster_ConnectList[] =
{
// string is "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. bytelen, data, and checksum are added.
{"\x01\x04\xF0\x00\x00\x10", 6, 2}, // Collect PLC firmware info block MBRegisters[10] for 16 registers
{0,0,0} // end flag
};
ModbusMaster_Cmds ModbusMaster_MonitorList[] =
{
// string is "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. bytelen, data, and checksum are added as necessary.
{"\x01\x04\xE0\x00\x00\x01", 6, 0}, // Read inputs to MBRegisters[0]
{"\x01\x10\xE2\x00\x00\x01", 6, 1}, // Write outputs from MBRegisters[1]
{"\x01\x03\x00\x00\x00\x01", 6, 2}, // Read DS1
{"\x01\x10\x00\x01\x00\x01", 6, 3}, // Write DS2
{0,0,0} // end flag
};
void ModbusMaster_Init()
{
printf("\nModbus Master Init\n");
EnableRS232Cmds(RS232_BAUD_38400);
DoRS232Cmds = FALSE;  // turn off processing RS232 input as commands
ModbusMaster_LastInTime=Time_sec();
ModbusMaster_EndOfPacketWait=3.5*10.0/38400; // wait 3.5 characters after a packet
ModbusMaster_packetSize=0;
 
ModbusMaster_Idle=0;
ModbusMaster_SentPtr=&ModbusMaster_ConnectList[0];
int c;
for (c=0;c<N_MB_REGISTERS;c++)
  MBRegisters[c]=0;
 
// make the register static arrays available to the other threads
persist.UserData[PERSIST_MBREG_BLOCK_ADR]=(int)MBRegisters;
//d printf("persist.UserData[%d]<=%08X\n",PERSIST_RWREG_BLOCK_ADR,MBRWRegisters); //debug
}
char* strncpy(char *dst,char* src,int len)
{
int i;
for (i=0;i<len;i++)
  dst[i]=src[i];
return dst;
}
// marshal and move values read from PLC/Slave into MBRegisters to KFlop memory
void ModbusMaster_RegUnload()
{
// Move 8 PLC inputs to virtual bits via MBRegisters[0]
// Note use SetStateBit which is Atomic and Thread Safe
 
int i;
if(delayT2 < Time_sec()) // anything in here only gets handled once every tDelay seconds
  {
delayT2 = Time_sec()+2;
  //printf("Input dump%d\n", MBRegisters[0]);
if(MBRegisters[0] & 32){
  //printf("&32Door Closed\n");
}
if(MBRegisters[0] & 64){
  //printf("&64Lube Low\n");
}
}
//for (i=0; i<8; i++)
// SetStateBit(48+i,(MBRegisters[0]>>i)&1);  // 8 input bits
}
// marshal and move values to be sent to PLC/Slave into MBRegisters
void ModbusMaster_RegLoad()
{
// Move 6 virtual bits to PLC outputs via MBRegisters[1]
MBRegisters[1] = (VirtualBits>>8)&0x3F; // the six bits after the 8 input bits
int tcd = *TCDesired;
MBRegisters[3] = *TCDesired;
if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
{
  printf("Desired-%d\n",tcd);
  delayT = Time_sec()+2;
}
}
void ModbusMaster_RegTransfer()
{
int tc = MBRegisters[2];
*TCPosition = tc;
if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
{
  //printf("CLICK Register-%d\n",tc);
  //delayT = Time_sec()+2;
}
}
void ModbusMaster_RegWrite()
{
//MBRegisters[3] = persist.UserData[12];
MBRegisters[3] = 0x06;
}
static unsigned char auchCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40
} ;
static char auchCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40
} ;

unsigned short CRC16(unsigned char *puchMsg,unsigned short usDataLen)
{
unsigned char uchCRCHi = 0xff;
unsigned char uchCRCLo = 0xff;
unsigned int uIndex;
while(usDataLen--)
{
  uIndex = uchCRCLo ^ *puchMsg++;
  uchCRCLo = uchCRCHi ^ auchCRCHi[uIndex];
  uchCRCHi = auchCRCLo[uIndex];
}
return (uchCRCHi<<8|uchCRCLo);
}
void ModbusMaster_NextCmd(MBErrors ecode)
{
if (ecode) printf("ModbusMaster_NextCmd(%d)\n",ecode); //debug
ModbusMaster_Idle=0; // ready to send a new command
if (INTERROR_TIMEOUT==ecode)
{
  if (ModbusMaster_List)
   ModbusMaster_Connected=0;
}
if (ModbusMaster_List)
{
  ModbusMaster_MonitorIndex++;
  if (!ModbusMaster_MonitorList[ModbusMaster_MonitorIndex].start)
  {
   ModbusMaster_MonitorIndex=0;
   ModbusMaster_MonitorCycleTime=Time_sec()-ModbusMaster_MonitorStartTime;
   ModbusMaster_MonitorStartTime=Time_sec();
  }
}
else
{
  ModbusMaster_ConnectIndex++;
  if (!ModbusMaster_ConnectList[ModbusMaster_ConnectIndex].start)
   ModbusMaster_List=1; // continue monitor list, do not restart here
}
if (INTERROR_TIMEOUT!=ecode&&0==ModbusMaster_Connected)
{
  ModbusMaster_Connected=1;
  ModbusMaster_List=0;
  ModbusMaster_ConnectIndex=0;
  ModbusMaster_TallyConnections++;
  ModbusMaster_TallyCommands=0;
  ModbusMaster_TallyRetries=0;
}
if (!ModbusMaster_List)
  ModbusMaster_SentPtr=&ModbusMaster_ConnectList[ModbusMaster_ConnectIndex];
else
  ModbusMaster_SentPtr=&ModbusMaster_MonitorList[ModbusMaster_MonitorIndex];
}
void ModbusMaster_Send(int verbose)
{
// send the command currently pointed to by ModbusMaster_SentPtr
// printf("ModbusMaster_Send(%d)\n",verbose);
 
char *chp;
int x;
unsigned char *xp;
if (!ModbusMaster_List)
  printf("List:%d ConnectIndex:%d MonitorIndex:%d\n",
    ModbusMaster_List,ModbusMaster_ConnectIndex,ModbusMaster_MonitorIndex);
if (!ModbusMaster_SentPtr->start)
{
  printf("ModbusMaster_Send: Tried to execute at end list\n");
  ModbusMaster_NextCmd(MBERROR_NONE);
}
 
strncpy(ModbusMaster_packetBuild,ModbusMaster_SentPtr->start,ModbusMaster_SentPtr->len);
chp=&ModbusMaster_packetBuild[ModbusMaster_SentPtr->len];
switch(ModbusMaster_packetBuild[1])
{
  case 0x10: // RW Write
   ModbusMaster_RegLoad();
   *chp++=ModbusMaster_packetBuild[5]*2;
   for (x=0;x<ModbusMaster_packetBuild[5];x++)
   {
    *chp++=(MBRegisters[x+ModbusMaster_SentPtr->reg]>>8)&0xFF;
    *chp++=MBRegisters[x+ModbusMaster_SentPtr->reg]&0xFF;
   }
   break;
  case 0x02: // Bit Read
  case 0x03: // RO Read
  case 0x04: // RW Read
   break;
  default:
   printf("Unexpected default: ModbusMaster_Send(), Function %d\n",ModbusMaster_packetBuild[1]); //debug
   break;
}
int csum=CRC16(ModbusMaster_packetBuild,chp-ModbusMaster_packetBuild);
*chp++=csum&0xFF;
*chp++=(csum>>8)&0xFF;
if (verbose) printf("Tx:"); //debug
for (xp=ModbusMaster_packetBuild;xp<chp;xp++)
{
  RS232_PutChar(*xp);
  if (verbose) printf("%02x;",*xp); //debug
}
if (verbose) printf("\n"); //debug
 
ModbusMaster_LastInTime=Time_sec();
ModbusMaster_Idle=1;
}
MBErrors Process_Data(unsigned char *Buffer, unsigned char Count)
{
int regndx;
int cnt;
int x;
unsigned short CRC = (((Buffer[Count-1]<<8)&0xFF00)|(Buffer[Count-2]&0xFF)); // Received CRC
unsigned short Recalculated_CRC = CRC16(Buffer,Count-2); // Computed CRC
if(Recalculated_CRC != CRC)
{
  printf("Count %d\n",Count);
  printf("Checksum: Theirs:%04X Mine:%04X, %d chars\n",CRC,Recalculated_CRC,Count); //debug
  return INTERROR_CHECKSUM;
}
//d printf("Packet %d\n",Function);    //debug
switch(Buffer[1])
{
  case 0x02:
   //ModbusMaster_BitUnload();
   break;
  case 0x03:
   //printf((int)Buffer[3]);
   //printf("Buffers-",Buffer[2]);
   cnt=Buffer[2];
   //printf("Buffers-%d\n",cnt);
   //MBRegisters[2]=Buffer[4];
   MBRegisters[2]=((Buffer[3]<<8)&0xFF00)|(Buffer[4]&0x00FF);
   //printf("Register-%d\n",MBRegisters[2]);
   ModbusMaster_RegTransfer();
   break;
  case 0x04:
   regndx=ModbusMaster_SentPtr->reg;
   cnt=Buffer[2];
   //printf("Buffers-%d\n",cnt);
   //printf(cnt);
   for (x=0;x<cnt;x+=2)
    MBRegisters[regndx++]=((Buffer[3+x]<<8)&0xFF00)|(Buffer[4+x]&0x00FF);
   //printf(MBRegisters[1]);
   ModbusMaster_RegUnload();
   break;
  case 0x10:
   // no action on successful write
   break;
  default:
   printf("Unexpected default: Process_Data(), Buffer[1]=%d\n",Buffer[1]);
   break;
}
 
return MBERROR_NONE; //We made it to the end, return
}
 
void ModbusMaster_Monitor()
{
char c;
if (pRS232RecIn != pRS232RecOut)
{
  ModbusMaster_LastInTime=Time_sec();
  while (pRS232RecIn != pRS232RecOut) // data in buffer
  {
   c=RS232_GetChar();
   if (ModbusMaster_packetSize<255)
    ModbusMaster_packetBuild[ModbusMaster_packetSize++]=c;
   //d printf("%02x,",c&0xFF); //debug
  }
}
else
{
  if (ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait<Time_sec() && ModbusMaster_packetSize)
  {
   int rtrn=Process_Data(ModbusMaster_packetBuild,ModbusMaster_packetSize);
   ModbusMaster_packetSize=0; // ready for next packet
   if (!rtrn)
   {
    ModbusMaster_TallyCommands++;
    ModbusMaster_NextCmd(rtrn);
    return;
   }
   printf("Error=%d, %f\n",rtrn,ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait-Time_sec()); //debug
   //d printf("\n",c); //debug
  }
  if (ModbusMaster_LastInTime+ModbusMaster_Timeout<Time_sec()) // retry test
  {
   ModbusMaster_Retry++;
   ModbusMaster_TallyRetries++;
   printf("ModbusMaster_Retry:%d\n",ModbusMaster_Retry); //debug
   if (ModbusMaster_Retry>ModbusMaster_MaxRetry)
   {
    //d printf("Failed Monitor message %d\n",ModbusMaster_MonitorIndex); //debug
    //ModbusMaster_ConnectIndex=0; // reset connection
    ModbusMaster_NextCmd(INTERROR_TIMEOUT);
   }
   else
   {
    ModbusMaster_Send(1);
   }
  }
}
}

void ModbusMaster_Loop()
{
//d printf("ModbusMaster_Loop: ModbusMaster_Idle=%d\n",ModbusMaster_Idle); //debug
if(!ModbusMaster_Idle)
{
  int x;
  //if (ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait>Time_sec())
  // ;
  //printf("ModbusMaster_Loop: ModbusMaster_Idle=%d\n",ModbusMaster_Idle); //debug
  ModbusMaster_Send(0);
  //for (x=0;x<16;x++)
  //{
  // printf("%04d ",(unsigned)MBRegisters[x]);
  // if ((x&7)==7) printf("\n");
  //}
  ModbusMaster_Retry=0;
}
else
  ModbusMaster_Monitor();
}
// End MODbus
 
int main()
{
ModbusMaster_Init();
int reportsecs=10;
double starttime;;
double MonitorStartTime=0; // start of most recent monitor cycle
double MonitorCycleTime=0; // seconds to call all commands in Monitor list
int TallyConnections=0; // Number of times Connecion list has been sent
int TallyCommands=0; // Commands since connection
int TallyRetries=0; // Retries since connection
starttime=Time_sec();
TallyCommands=ModbusMaster_TallyCommands;
for(;;) // loop forever
{
  ModbusMaster_Loop();
  if (starttime+reportsecs<Time_sec())
  {
   printf("\nSeconds: %d\n",reportsecs);
   printf("ModbusMaster_MonitorCycleTime=%f (%f/s)\n",ModbusMaster_MonitorCycleTime,1.0/ModbusMaster_MonitorCycleTime);
   printf("ModbusMaster_TallyCommands/s=%0.lf\n",(ModbusMaster_TallyCommands-TallyCommands)/(double)reportsecs);
   printf("ModbusMaster_TallyConnections=%d\n",ModbusMaster_TallyConnections);
   printf("ModbusMaster_TallyRetries=%d\n",ModbusMaster_TallyRetries);
 
   starttime=Time_sec();
   TallyCommands=ModbusMaster_TallyCommands;
  }
} // end of forever
    return 0;
}
 
Moray
 
  if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
  {
   //printf("tick\n");
   delayT = Time_sec()+tDelay;
  }
 
 

 
On Tue, Jul 29, 2014 at 9:09 PM, engnerdan@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
 

There was a discussion on here a while back initiated by Moray about adding some additional modbus addresses/functions to the modbus code example. I am looking to do the same thing but I am incredibly lost looking at this example program (no C experience before). If Moray or someone else could share their code and maybe help walk me through what is happening in the code that would be great.


-Dan

dan@...

 
Group: DynoMotion Message: 9893 From: Tom Kerekes Date: 8/1/2014
Subject: Re: Expanding on the Modbus RTU Example
Hi Dan,

Although the Modbus code looks complicated you should only need minor changes to achieve what you describe.

There basically two things that need to be modified:

#1 - decide which registers need to read/and written in your Modbus device.  You will need to research your particular Modbus device to obtain this information.  Then Modify the table called ModbusMaster_MonitorList so these registers are are read and written.  This should be all that is required to magically transfer the data to/from the Modbus device and the internal MBRegister[] array within KFLOP.

#2 - code the RegLoad() and RegUnload() functions to move data within KFLOP to/from the MBRegister Array.  In your example you would be reading the ADC() values and placing them into the MBRegister Array.

HTH
Regards
TK


From: "'Dan' engnerdan@... [DynoMotion]" <DynoMotion@yahoogroups.com>
To: DynoMotion@yahoogroups.com
Sent: Thursday, July 31, 2014 2:29 PM
Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example

 
Moray,
Thanks I will take a look at the code later. What I am trying to do is send the ADC value from 4 of the Kanalog inputs to the PLC and then have an HMI connected to the PLC display those values as moving bar graphs. These are to show the load on each of my servos and my spindle. I was going to try and modify the KmotionCNC GUI to add the bar graph load meters, but I have too much to learn with C that I don’t need to try working on C++ and the GUI too.
 
Additionally I think it would require that I rewrite the GUI code each time there is a version update. The additional digital I/O of the PLC will not hurt any either, I am out of I/O on my Kanalog and have not even started designing my tool changer.
 
 
Dan
 
Sent: Tuesday, July 29, 2014 4:42 PM
Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
 
 
Hi,
 
I just had to dig out a backup of my main init file to figure out how I eventually implemented things. It is some quite complicated code, as there is quite a bit of error checking/handling involved, and I can't remember exactly everything that I had to change to get it to work.
I've copied in the relevant code from my init file, so you can compare it to the supplied modbus master example to see where I've added bits (I'd go through it and check, but I've not got the time as I'm currently in the middle of a new machine build!)
 
For my application, I have a Click PLC with an additional IO module to control my toolchanger, and provide some additional inputs. All I'm doing via modbus, is read the current tool position from a register, write the desired position to a register, and read the current status of the inputs on the additional IO, so I'm only dealing with 3 registers.
 
From a quick scan through, I use RegUnload() to read the additional input register to test for active bits which correspond to the inputs.
RegLoad copies the desired tool position into a register (*TCDesired and *TCPosition are pointers to user data locations which are used for transfer to/from Mach)
RegTransfer is used to read the current tool position from a register to the relevant userdata.
 
What registers are actually read/written, is setup in ModbusMaster_Cmds ModbusMaster_MonitorList[]
Each line translates to a single MBregisters[] which is then used in the RegLoad/Unload functions, which explains why I'm referencing specific array indexes within those functions.
The key options/commands (cmd) are 3 - read input register, 4 read register (as in memory location), 10 write register (this can be any writeable register i.e. memory or output).
 
I think that's all the basics covered. I did add a good bit debug code when doing this, so if you think there's something there that's not needed, then that's most likely why!
 
Here's the code-
 
// Start MODbus
#include "RS232/ModBus/ModBusMaster.h"
#define TCPOS 5
#define TCDES 6

unsigned short MBRegisters[N_MB_REGISTERS];
double delayT = 0;
double delayT2 = 0;
// Constants
int ModbusMaster_MaxRetry=3;
double ModbusMaster_Timeout=0.5; //seconds for no response for a send command
double ModbusMaster_ResponseTime; //seconds for last PLC response
double ModbusMaster_CommandSentTime;
// status and performance counters
double ModbusMaster_MonitorStartTime=0; // start of most recent monitor cycle
double ModbusMaster_MonitorCycleTime=0; // seconds to call all commands in Monitor list
int ModbusMaster_TallyConnections=0; // Number of times Connection list has been sent
int ModbusMaster_TallyCommands=0; // Commands since connection
int ModbusMaster_TallyRetries=0; // Retries since connection
// statuses, counters, etc
int ModbusMaster_List=0; // 0=connect, 1=monitor
int ModbusMaster_Connected=0;
int ModbusMaster_MonitorIndex=0;
int ModbusMaster_ConnectIndex=0;
int ModbusMaster_Idle=0; // 0=idle, 1=await reply
int ModbusMaster_Retry=0;
double ModbusMaster_LastInTime=0;
double ModbusMaster_EndOfPacketWait=3.5*10/9600; // wait 3.5 characters after a packet 9600=baud rate)
char ModbusMaster_packetBuild[256]; // max length of modbus frame
int ModbusMaster_packetSize=0;
double *TCPosition  = (double *)&persist.UserData[(TCPOS -1)*2];
double *TCDesired = (double *)&persist.UserData[(TCDES-1)*2];
   
typedef enum
{
MBERROR_NONE = 0,
// Modbus codes; reported with Modbus error packet
MBERROR_ILLEGAL_FUNCTION = 1,
MBERROR_ILLEGAL_DATA_ADDRESS = 2, // used
MBERROR_ILLEGAL_DATA_VALUE = 3, // used
MBERROR_SLAVE_DEVICE_FAILURE = 4,
MBERROR_ACKNOWLEDGE = 5,
MBERROR_SLAVE_DEVICE_BUSY = 6,
MBERROR_NEGATIVE_ACKNOWLEDGE = 7,
MBERROR_MEMORY_PARITY_ERROR = 8,
// internal codes
INTERROR_WRONG_DEVICE = 9,
INTERROR_CHECKSUM = 10,
INTERROR_TIMEOUT = 11,
} MBErrors;

typedef struct ModbusMaster_sCmds
{
char *start; // has "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. Not data and Not checksum.
int len; // length of start string. commonly 6
int reg; // reg# is the start index into the MBRegisters array for the command
} ModbusMaster_Cmds;
ModbusMaster_Cmds *ModbusMaster_SentPtr; // pointer to current command. used to send, resend, interpret response.
ModbusMaster_Cmds ModbusMaster_ConnectList[] =
{
// string is "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. bytelen, data, and checksum are added.
{"\x01\x04\xF0\x00\x00\x10", 6, 2}, // Collect PLC firmware info block MBRegisters[10] for 16 registers
{0,0,0} // end flag
};
ModbusMaster_Cmds ModbusMaster_MonitorList[] =
{
// string is "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. bytelen, data, and checksum are added as necessary.
{"\x01\x04\xE0\x00\x00\x01", 6, 0}, // Read inputs to MBRegisters[0]
{"\x01\x10\xE2\x00\x00\x01", 6, 1}, // Write outputs from MBRegisters[1]
{"\x01\x03\x00\x00\x00\x01", 6, 2}, // Read DS1
{"\x01\x10\x00\x01\x00\x01", 6, 3}, // Write DS2
{0,0,0} // end flag
};
void ModbusMaster_Init()
{
printf("\nModbus Master Init\n");
EnableRS232Cmds(RS232_BAUD_38400);
DoRS232Cmds = FALSE;  // turn off processing RS232 input as commands
ModbusMaster_LastInTime=Time_sec();
ModbusMaster_EndOfPacketWait=3.5*10.0/38400; // wait 3.5 characters after a packet
ModbusMaster_packetSize=0;
 
ModbusMaster_Idle=0;
ModbusMaster_SentPtr=&ModbusMaster_ConnectList[0];
int c;
for (c=0;c<N_MB_REGISTERS;c++)
  MBRegisters[c]=0;
 
// make the register static arrays available to the other threads
persist.UserData[PERSIST_MBREG_BLOCK_ADR]=(int)MBRegisters;
//d printf("persist.UserData[%d]<=%08X\n",PERSIST_RWREG_BLOCK_ADR,MBRWRegisters); //debug
}
char* strncpy(char *dst,char* src,int len)
{
int i;
for (i=0;i<len;i++)
  dst[i]=src[i];
return dst;
}
// marshal and move values read from PLC/Slave into MBRegisters to KFlop memory
void ModbusMaster_RegUnload()
{
// Move 8 PLC inputs to virtual bits via MBRegisters[0]
// Note use SetStateBit which is Atomic and Thread Safe
 
int i;
if(delayT2 < Time_sec()) // anything in here only gets handled once every tDelay seconds
  {
delayT2 = Time_sec()+2;
  //printf("Input dump%d\n", MBRegisters[0]);
if(MBRegisters[0] & 32){
  //printf("&32Door Closed\n");
}
if(MBRegisters[0] & 64){
  //printf("&64Lube Low\n");
}
}
//for (i=0; i<8; i++)
// SetStateBit(48+i,(MBRegisters[0]>>i)&1);  // 8 input bits
}
// marshal and move values to be sent to PLC/Slave into MBRegisters
void ModbusMaster_RegLoad()
{
// Move 6 virtual bits to PLC outputs via MBRegisters[1]
MBRegisters[1] = (VirtualBits>>8)&0x3F; // the six bits after the 8 input bits
int tcd = *TCDesired;
MBRegisters[3] = *TCDesired;
if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
{
  printf("Desired-%d\n",tcd);
  delayT = Time_sec()+2;
}
}
void ModbusMaster_RegTransfer()
{
int tc = MBRegisters[2];
*TCPosition = tc;
if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
{
  //printf("CLICK Register-%d\n",tc);
  //delayT = Time_sec()+2;
}
}
void ModbusMaster_RegWrite()
{
//MBRegisters[3] = persist.UserData[12];
MBRegisters[3] = 0x06;
}
static unsigned char auchCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
0x40
} ;
static char auchCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
0x40
} ;

unsigned short CRC16(unsigned char *puchMsg,unsigned short usDataLen)
{
unsigned char uchCRCHi = 0xff;
unsigned char uchCRCLo = 0xff;
unsigned int uIndex;
while(usDataLen--)
{
  uIndex = uchCRCLo ^ *puchMsg++;
  uchCRCLo = uchCRCHi ^ auchCRCHi[uIndex];
  uchCRCHi = auchCRCLo[uIndex];
}
return (uchCRCHi<<8|uchCRCLo);
}
void ModbusMaster_NextCmd(MBErrors ecode)
{
if (ecode) printf("ModbusMaster_NextCmd(%d)\n",ecode); //debug
ModbusMaster_Idle=0; // ready to send a new command
if (INTERROR_TIMEOUT==ecode)
{
  if (ModbusMaster_List)
   ModbusMaster_Connected=0;
}
if (ModbusMaster_List)
{
  ModbusMaster_MonitorIndex++;
  if (!ModbusMaster_MonitorList[ModbusMaster_MonitorIndex].start)
  {
   ModbusMaster_MonitorIndex=0;
   ModbusMaster_MonitorCycleTime=Time_sec()-ModbusMaster_MonitorStartTime;
   ModbusMaster_MonitorStartTime=Time_sec();
  }
}
else
{
  ModbusMaster_ConnectIndex++;
  if (!ModbusMaster_ConnectList[ModbusMaster_ConnectIndex].start)
   ModbusMaster_List=1; // continue monitor list, do not restart here
}
if (INTERROR_TIMEOUT!=ecode&&0==ModbusMaster_Connected)
{
  ModbusMaster_Connected=1;
  ModbusMaster_List=0;
  ModbusMaster_ConnectIndex=0;
  ModbusMaster_TallyConnections++;
  ModbusMaster_TallyCommands=0;
  ModbusMaster_TallyRetries=0;
}
if (!ModbusMaster_List)
  ModbusMaster_SentPtr=&ModbusMaster_ConnectList[ModbusMaster_ConnectIndex];
else
  ModbusMaster_SentPtr=&ModbusMaster_MonitorList[ModbusMaster_MonitorIndex];
}
void ModbusMaster_Send(int verbose)
{
// send the command currently pointed to by ModbusMaster_SentPtr
// printf("ModbusMaster_Send(%d)\n",verbose);
 
char *chp;
int x;
unsigned char *xp;
if (!ModbusMaster_List)
  printf("List:%d ConnectIndex:%d MonitorIndex:%d\n",
    ModbusMaster_List,ModbusMaster_ConnectIndex,ModbusMaster_MonitorIndex);
if (!ModbusMaster_SentPtr->start)
{
  printf("ModbusMaster_Send: Tried to execute at end list\n");
  ModbusMaster_NextCmd(MBERROR_NONE);
}
 
strncpy(ModbusMaster_packetBuild,ModbusMaster_SentPtr->start,ModbusMaster_SentPtr->len);
chp=&ModbusMaster_packetBuild[ModbusMaster_SentPtr->len];
switch(ModbusMaster_packetBuild[1])
{
  case 0x10: // RW Write
   ModbusMaster_RegLoad();
   *chp++=ModbusMaster_packetBuild[5]*2;
   for (x=0;x<ModbusMaster_packetBuild[5];x++)
   {
    *chp++=(MBRegisters[x+ModbusMaster_SentPtr->reg]>>8)&0xFF;
    *chp++=MBRegisters[x+ModbusMaster_SentPtr->reg]&0xFF;
   }
   break;
  case 0x02: // Bit Read
  case 0x03: // RO Read
  case 0x04: // RW Read
   break;
  default:
   printf("Unexpected default: ModbusMaster_Send(), Function %d\n",ModbusMaster_packetBuild[1]); //debug
   break;
}
int csum=CRC16(ModbusMaster_packetBuild,chp-ModbusMaster_packetBuild);
*chp++=csum&0xFF;
*chp++=(csum>>8)&0xFF;
if (verbose) printf("Tx:"); //debug
for (xp=ModbusMaster_packetBuild;xp<chp;xp++)
{
  RS232_PutChar(*xp);
  if (verbose) printf("%02x;",*xp); //debug
}
if (verbose) printf("\n"); //debug
 
ModbusMaster_LastInTime=Time_sec();
ModbusMaster_Idle=1;
}
MBErrors Process_Data(unsigned char *Buffer, unsigned char Count)
{
int regndx;
int cnt;
int x;
unsigned short CRC = (((Buffer[Count-1]<<8)&0xFF00)|(Buffer[Count-2]&0xFF)); // Received CRC
unsigned short Recalculated_CRC = CRC16(Buffer,Count-2); // Computed CRC
if(Recalculated_CRC != CRC)
{
  printf("Count %d\n",Count);
  printf("Checksum: Theirs:%04X Mine:%04X, %d chars\n",CRC,Recalculated_CRC,Count); //debug
  return INTERROR_CHECKSUM;
}
//d printf("Packet %d\n",Function);    //debug
switch(Buffer[1])
{
  case 0x02:
   //ModbusMaster_BitUnload();
   break;
  case 0x03:
   //printf((int)Buffer[3]);
   //printf("Buffers-",Buffer[2]);
   cnt=Buffer[2];
   //printf("Buffers-%d\n",cnt);
   //MBRegisters[2]=Buffer[4];
   MBRegisters[2]=((Buffer[3]<<8)&0xFF00)|(Buffer[4]&0x00FF);
   //printf("Register-%d\n",MBRegisters[2]);
   ModbusMaster_RegTransfer();
   break;
  case 0x04:
   regndx=ModbusMaster_SentPtr->reg;
   cnt=Buffer[2];
   //printf("Buffers-%d\n",cnt);
   //printf(cnt);
   for (x=0;x<cnt;x+=2)
    MBRegisters[regndx++]=((Buffer[3+x]<<8)&0xFF00)|(Buffer[4+x]&0x00FF);
   //printf(MBRegisters[1]);
   ModbusMaster_RegUnload();
   break;
  case 0x10:
   // no action on successful write
   break;
  default:
   printf("Unexpected default: Process_Data(), Buffer[1]=%d\n",Buffer[1]);
   break;
}
 
return MBERROR_NONE; //We made it to the end, return
}
 
void ModbusMaster_Monitor()
{
char c;
if (pRS232RecIn != pRS232RecOut)
{
  ModbusMaster_LastInTime=Time_sec();
  while (pRS232RecIn != pRS232RecOut) // data in buffer
  {
   c=RS232_GetChar();
   if (ModbusMaster_packetSize<255)
    ModbusMaster_packetBuild[ModbusMaster_packetSize++]=c;
   //d printf("%02x,",c&0xFF); //debug
  }
}
else
{
  if (ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait<Time_sec() && ModbusMaster_packetSize)
  {
   int rtrn=Process_Data(ModbusMaster_packetBuild,ModbusMaster_packetSize);
   ModbusMaster_packetSize=0; // ready for next packet
   if (!rtrn)
   {
    ModbusMaster_TallyCommands++;
    ModbusMaster_NextCmd(rtrn);
    return;
   }
   printf("Error=%d, %f\n",rtrn,ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait-Time_sec()); //debug
   //d printf("\n",c); //debug
  }
  if (ModbusMaster_LastInTime+ModbusMaster_Timeout<Time_sec()) // retry test
  {
   ModbusMaster_Retry++;
   ModbusMaster_TallyRetries++;
   printf("ModbusMaster_Retry:%d\n",ModbusMaster_Retry); //debug
   if (ModbusMaster_Retry>ModbusMaster_MaxRetry)
   {
    //d printf("Failed Monitor message %d\n",ModbusMaster_MonitorIndex); //debug
    //ModbusMaster_ConnectIndex=0; // reset connection
    ModbusMaster_NextCmd(INTERROR_TIMEOUT);
   }
   else
   {
    ModbusMaster_Send(1);
   }
  }
}
}

void ModbusMaster_Loop()
{
//d printf("ModbusMaster_Loop: ModbusMaster_Idle=%d\n",ModbusMaster_Idle); //debug
if(!ModbusMaster_Idle)
{
  int x;
  //if (ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait>Time_sec())
  // ;
  //printf("ModbusMaster_Loop: ModbusMaster_Idle=%d\n",ModbusMaster_Idle); //debug
  ModbusMaster_Send(0);
  //for (x=0;x<16;x++)
  //{
  // printf("%04d ",(unsigned)MBRegisters[x]);
  // if ((x&7)==7) printf("\n");
  //}
  ModbusMaster_Retry=0;
}
else
  ModbusMaster_Monitor();
}
// End MODbus
 
int main()
{
ModbusMaster_Init();
int reportsecs=10;
double starttime;;
double MonitorStartTime=0; // start of most recent monitor cycle
double MonitorCycleTime=0; // seconds to call all commands in Monitor list
int TallyConnections=0; // Number of times Connecion list has been sent
int TallyCommands=0; // Commands since connection
int TallyRetries=0; // Retries since connection
starttime=Time_sec();
TallyCommands=ModbusMaster_TallyCommands;
for(;;) // loop forever
{
  ModbusMaster_Loop();
  if (starttime+reportsecs<Time_sec())
  {
   printf("\nSeconds: %d\n",reportsecs);
   printf("ModbusMaster_MonitorCycleTime=%f (%f/s)\n",ModbusMaster_MonitorCycleTime,1.0/ModbusMaster_MonitorCycleTime);
   printf("ModbusMaster_TallyCommands/s=%0.lf\n",(ModbusMaster_TallyCommands-TallyCommands)/(double)reportsecs);
   printf("ModbusMaster_TallyConnections=%d\n",ModbusMaster_TallyConnections);
   printf("ModbusMaster_TallyRetries=%d\n",ModbusMaster_TallyRetries);
 
   starttime=Time_sec();
   TallyCommands=ModbusMaster_TallyCommands;
  }
} // end of forever
    return 0;
}
 
Moray
 
  if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
  {
   //printf("tick\n");
   delayT = Time_sec()+tDelay;
  }
 
 

 
On Tue, Jul 29, 2014 at 9:09 PM, engnerdan@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
 
There was a discussion on here a while back initiated by Moray about adding some additional modbus addresses/functions to the modbus code example. I am looking to do the same thing but I am incredibly lost looking at this example program (no C experience before). If Moray or someone else could share their code and maybe help walk me through what is happening in the code that would be great.

-Dan
 


Group: DynoMotion Message: 9969 From: Dan Date: 8/14/2014
Subject: Re: Expanding on the Modbus RTU Example
Attachments :
    Tom,
    I tried compiling and running the Modbus code as it comes with the Kmotion software (no changes) and I am getting this error on the console screen. I had nothing else running on the KFLOP at the time. The PLC was an Automation Direct Do-More configured per the notes 38400,8,1, no parity. The function codes are suppose to be the same (Modicon based Modbus RTU). Do you have any insight into what this might mean?
     
    plc error
     
    Dan
     
    Sent: Friday, August 01, 2014 9:10 AM
    Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
     
     

    Hi Dan,

    Although the Modbus code looks complicated you should only need minor changes to achieve what you describe.

    There basically two things that need to be modified:

    #1 - decide which registers need to read/and written in your Modbus device.  You will need to research your particular Modbus device to obtain this information.  Then Modify the table called ModbusMaster_MonitorList so these registers are are read and written.  This should be all that is required to magically transfer the data to/from the Modbus device and the internal MBRegister[] array within KFLOP.

    #2 - code the RegLoad() and RegUnload() functions to move data within KFLOP to/from the MBRegister Array.  In your example you would be reading the ADC() values and placing them into the MBRegister Array.

    HTH
    Regards
    TK
     
    Group: DynoMotion Message: 9972 From: Tom Kerekes Date: 8/14/2014
    Subject: Re: Expanding on the Modbus RTU Example
    Hi Dan,

    In the received string from the device the 2nd byte (Buffer[1]) is normally expected to be either a value of 3, 4, or 16.  But it appears to be receiving a 132 and 144.  Which are probably meant to be a 4 and 16 but with 128 added (Bit7). 

    Maybe you have even parity selected on the device?

    Regards
    TK

    Group: DynoMotion Message: 9973 From: Dan Date: 8/15/2014
    Subject: Re: Expanding on the Modbus RTU Example [1 Attachment]
    Attachments :
      I double checked and parity is not on. I tried setting it to even, odd, off and the only time it is sending and receiving is when it is set to off. I changed every possible setting on the PLC and all that happens is the PLC stops transmitting back to the KFLOP, So my settings are correct. I have a serial communication card for this PLC with 2 additional RS232 ports, I got the same problem from the KFLOP when I tried those ports too. I am going to see if I can possibly get my hands on a Click PLC this weekend, but I think it will be next week before I can get one on loan from a previous employer.
       
      This is the PLC CPU I am trying to use.
       
      I did manage to get the beginning of message from the counsel window.
       
      Modbus failure 1
       
      Dan
       
      Sent: Thursday, August 14, 2014 4:36 PM
      Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example [1 Attachment]
       
       

      Hi Dan,

      In the received string from the device the 2nd byte (Buffer[1]) is normally expected to be either a value of 3, 4, or 16.  But it appears to be receiving a 132 and 144.  Which are probably meant to be a 4 and 16 but with 128 added (Bit7). 

      Maybe you have even parity selected on the device?

      Regards
      TK
       
      Group: DynoMotion Message: 9975 From: Tom Kerekes Date: 8/15/2014
      Subject: Re: Expanding on the Modbus RTU Example
      Hi Dan,

      I think you are probably correct regarding the no parity setting.  That message prints after the CRC (checksum) in the response string has been verified to be correct.

      I found this:


      It says that bit 8 is set in the response to indicate that there is an error.  I'm having a hard time finding the manual for the Automation Direct Modbus Software.  Were you able to find it?

      It may be that you are just trying to read/write invalid registers for your device.  I think you will need to determine which registers you want to read and change the tables to only read/write valid registers.

      HTH
      Regards
      TK



      From: "'Dan' engnerdan@... [DynoMotion]" <DynoMotion@yahoogroups.com>
      To: DynoMotion@yahoogroups.com
      Sent: Friday, August 15, 2014 6:42 AM
      Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example

       
      I double checked and parity is not on. I tried setting it to even, odd, off and the only time it is sending and receiving is when it is set to off. I changed every possible setting on the PLC and all that happens is the PLC stops transmitting back to the KFLOP, So my settings are correct. I have a serial communication card for this PLC with 2 additional RS232 ports, I got the same problem from the KFLOP when I tried those ports too. I am going to see if I can possibly get my hands on a Click PLC this weekend, but I think it will be next week before I can get one on loan from a previous employer.
       
      This is the PLC CPU I am trying to use.
       
      I did manage to get the beginning of message from the counsel window.
       
      Modbus failure 1
       
      Dan
       
      Sent: Thursday, August 14, 2014 4:36 PM
      Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example [1 Attachment]
       
       
      Hi Dan,

      In the received string from the device the 2nd byte (Buffer[1]) is normally expected to be either a value of 3, 4, or 16.  But it appears to be receiving a 132 and 144.  Which are probably meant to be a 4 and 16 but with 128 added (Bit7). 

      Maybe you have even parity selected on the device?

      Regards
      TK
       

      From: "'Dan' engnerdan@... [DynoMotion]" <DynoMotion@yahoogroups.com>
      To: DynoMotion@yahoogroups.com
      Sent: Thursday, August 14, 2014 12:51 PM
      Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
       
       
      Tom,
      I tried compiling and running the Modbus code as it comes with the Kmotion software (no changes) and I am getting this error on the console screen. I had nothing else running on the KFLOP at the time. The PLC was an Automation Direct Do-More configured per the notes 38400,8,1, no parity. The function codes are suppose to be the same (Modicon based Modbus RTU). Do you have any insight into what this might mean?
       
      plc error
       
      Dan
       


      Sent: Friday, August 01, 2014 9:10 AM
      Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
       
       
      Hi Dan,

      Although the Modbus code looks complicated you should only need minor changes to achieve what you describe.

      There basically two things that need to be modified:

      #1 - decide which registers need to read/and written in your Modbus device.  You will need to research your particular Modbus device to obtain this information.  Then Modify the table called ModbusMaster_MonitorList so these registers are are read and written.  This should be all that is required to magically transfer the data to/from the Modbus device and the internal MBRegister[] array within KFLOP.

      #2 - code the RegLoad() and RegUnload() functions to move data within KFLOP to/from the MBRegister Array.  In your example you would be reading the ADC() values and placing them into the MBRegister Array.

      HTH
      Regards
      TK
       

      From: "'Dan' engnerdan@... [DynoMotion]" <DynoMotion@yahoogroups.com>
      To: DynoMotion@yahoogroups.com
      Sent: Thursday, July 31, 2014 2:29 PM
      Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
       
       
      Moray,
      Thanks I will take a look at the code later. What I am trying to do is send the ADC value from 4 of the Kanalog inputs to the PLC and then have an HMI connected to the PLC display those values as moving bar graphs. These are to show the load on each of my servos and my spindle. I was going to try and modify the KmotionCNC GUI to add the bar graph load meters, but I have too much to learn with C that I don’t need to try working on C++ and the GUI too.
       
      Additionally I think it would require that I rewrite the GUI code each time there is a version update. The additional digital I/O of the PLC will not hurt any either, I am out of I/O on my Kanalog and have not even started designing my tool changer.
       
       
      Dan
       
      Sent: Tuesday, July 29, 2014 4:42 PM
      Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
       
       
      Hi,
       
      I just had to dig out a backup of my main init file to figure out how I eventually implemented things. It is some quite complicated code, as there is quite a bit of error checking/handling involved, and I can't remember exactly everything that I had to change to get it to work.
      I've copied in the relevant code from my init file, so you can compare it to the supplied modbus master example to see where I've added bits (I'd go through it and check, but I've not got the time as I'm currently in the middle of a new machine build!)
       
      For my application, I have a Click PLC with an additional IO module to control my toolchanger, and provide some additional inputs. All I'm doing via modbus, is read the current tool position from a register, write the desired position to a register, and read the current status of the inputs on the additional IO, so I'm only dealing with 3 registers.
       
      From a quick scan through, I use RegUnload() to read the additional input register to test for active bits which correspond to the inputs.
      RegLoad copies the desired tool position into a register (*TCDesired and *TCPosition are pointers to user data locations which are used for transfer to/from Mach)
      RegTransfer is used to read the current tool position from a register to the relevant userdata.
       
      What registers are actually read/written, is setup in ModbusMaster_Cmds ModbusMaster_MonitorList[]
      Each line translates to a single MBregisters[] which is then used in the RegLoad/Unload functions, which explains why I'm referencing specific array indexes within those functions.
      The key options/commands (cmd) are 3 - read input register, 4 read register (as in memory location), 10 write register (this can be any writeable register i.e. memory or output).
       
      I think that's all the basics covered. I did add a good bit debug code when doing this, so if you think there's something there that's not needed, then that's most likely why!
       
      Here's the code-
       
      // Start MODbus
      #include "RS232/ModBus/ModBusMaster.h"
      #define TCPOS 5
      #define TCDES 6

      unsigned short MBRegisters[N_MB_REGISTERS];
      double delayT = 0;
      double delayT2 = 0;
      // Constants
      int ModbusMaster_MaxRetry=3;
      double ModbusMaster_Timeout=0.5; //seconds for no response for a send command
      double ModbusMaster_ResponseTime; //seconds for last PLC response
      double ModbusMaster_CommandSentTime;
      // status and performance counters
      double ModbusMaster_MonitorStartTime=0; // start of most recent monitor cycle
      double ModbusMaster_MonitorCycleTime=0; // seconds to call all commands in Monitor list
      int ModbusMaster_TallyConnections=0; // Number of times Connection list has been sent
      int ModbusMaster_TallyCommands=0; // Commands since connection
      int ModbusMaster_TallyRetries=0; // Retries since connection
      // statuses, counters, etc
      int ModbusMaster_List=0; // 0=connect, 1=monitor
      int ModbusMaster_Connected=0;
      int ModbusMaster_MonitorIndex=0;
      int ModbusMaster_ConnectIndex=0;
      int ModbusMaster_Idle=0; // 0=idle, 1=await reply
      int ModbusMaster_Retry=0;
      double ModbusMaster_LastInTime=0;
      double ModbusMaster_EndOfPacketWait=3.5*10/9600; // wait 3.5 characters after a packet 9600=baud rate)
      char ModbusMaster_packetBuild[256]; // max length of modbus frame
      int ModbusMaster_packetSize=0;
      double *TCPosition  = (double *)&persist.UserData[(TCPOS -1)*2];
      double *TCDesired = (double *)&persist.UserData[(TCDES-1)*2];
         
      typedef enum
      {
      MBERROR_NONE = 0,
      // Modbus codes; reported with Modbus error packet
      MBERROR_ILLEGAL_FUNCTION = 1,
      MBERROR_ILLEGAL_DATA_ADDRESS = 2, // used
      MBERROR_ILLEGAL_DATA_VALUE = 3, // used
      MBERROR_SLAVE_DEVICE_FAILURE = 4,
      MBERROR_ACKNOWLEDGE = 5,
      MBERROR_SLAVE_DEVICE_BUSY = 6,
      MBERROR_NEGATIVE_ACKNOWLEDGE = 7,
      MBERROR_MEMORY_PARITY_ERROR = 8,
      // internal codes
      INTERROR_WRONG_DEVICE = 9,
      INTERROR_CHECKSUM = 10,
      INTERROR_TIMEOUT = 11,
      } MBErrors;

      typedef struct ModbusMaster_sCmds
      {
      char *start; // has "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. Not data and Not checksum.
      int len; // length of start string. commonly 6
      int reg; // reg# is the start index into the MBRegisters array for the command
      } ModbusMaster_Cmds;
      ModbusMaster_Cmds *ModbusMaster_SentPtr; // pointer to current command. used to send, resend, interpret response.
      ModbusMaster_Cmds ModbusMaster_ConnectList[] =
      {
      // string is "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. bytelen, data, and checksum are added.
      {"\x01\x04\xF0\x00\x00\x10", 6, 2}, // Collect PLC firmware info block MBRegisters[10] for 16 registers
      {0,0,0} // end flag
      };
      ModbusMaster_Cmds ModbusMaster_MonitorList[] =
      {
      // string is "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. bytelen, data, and checksum are added as necessary.
      {"\x01\x04\xE0\x00\x00\x01", 6, 0}, // Read inputs to MBRegisters[0]
      {"\x01\x10\xE2\x00\x00\x01", 6, 1}, // Write outputs from MBRegisters[1]
      {"\x01\x03\x00\x00\x00\x01", 6, 2}, // Read DS1
      {"\x01\x10\x00\x01\x00\x01", 6, 3}, // Write DS2
      {0,0,0} // end flag
      };
      void ModbusMaster_Init()
      {
      printf("\nModbus Master Init\n");
      EnableRS232Cmds(RS232_BAUD_38400);
      DoRS232Cmds = FALSE;  // turn off processing RS232 input as commands
      ModbusMaster_LastInTime=Time_sec();
      ModbusMaster_EndOfPacketWait=3.5*10.0/38400; // wait 3.5 characters after a packet
      ModbusMaster_packetSize=0;
       
      ModbusMaster_Idle=0;
      ModbusMaster_SentPtr=&ModbusMaster_ConnectList[0];
      int c;
      for (c=0;c<N_MB_REGISTERS;c++)
        MBRegisters[c]=0;
       
      // make the register static arrays available to the other threads
      persist.UserData[PERSIST_MBREG_BLOCK_ADR]=(int)MBRegisters;
      //d printf("persist.UserData[%d]<=%08X\n",PERSIST_RWREG_BLOCK_ADR,MBRWRegisters); //debug
      }
      char* strncpy(char *dst,char* src,int len)
      {
      int i;
      for (i=0;i<len;i++)
        dst[i]=src[i];
      return dst;
      }
      // marshal and move values read from PLC/Slave into MBRegisters to KFlop memory
      void ModbusMaster_RegUnload()
      {
      // Move 8 PLC inputs to virtual bits via MBRegisters[0]
      // Note use SetStateBit which is Atomic and Thread Safe
       
      int i;
      if(delayT2 < Time_sec()) // anything in here only gets handled once every tDelay seconds
        {
      delayT2 = Time_sec()+2;
        //printf("Input dump%d\n", MBRegisters[0]);
      if(MBRegisters[0] & 32){
        //printf("&32Door Closed\n");
      }
      if(MBRegisters[0] & 64){
        //printf("&64Lube Low\n");
      }
      }
      //for (i=0; i<8; i++)
      // SetStateBit(48+i,(MBRegisters[0]>>i)&1);  // 8 input bits
      }
      // marshal and move values to be sent to PLC/Slave into MBRegisters
      void ModbusMaster_RegLoad()
      {
      // Move 6 virtual bits to PLC outputs via MBRegisters[1]
      MBRegisters[1] = (VirtualBits>>8)&0x3F; // the six bits after the 8 input bits
      int tcd = *TCDesired;
      MBRegisters[3] = *TCDesired;
      if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
      {
        printf("Desired-%d\n",tcd);
        delayT = Time_sec()+2;
      }
      }
      void ModbusMaster_RegTransfer()
      {
      int tc = MBRegisters[2];
      *TCPosition = tc;
      if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
      {
        //printf("CLICK Register-%d\n",tc);
        //delayT = Time_sec()+2;
      }
      }
      void ModbusMaster_RegWrite()
      {
      //MBRegisters[3] = persist.UserData[12];
      MBRegisters[3] = 0x06;
      }
      static unsigned char auchCRCHi[] = {
      0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
      0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
      0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
      0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
      0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
      0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
      0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
      0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
      0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
      0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
      0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
      0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
      0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
      0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
      0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
      0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
      0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
      0x40
      } ;
      static char auchCRCLo[] = {
      0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
      0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
      0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
      0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
      0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
      0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
      0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
      0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
      0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
      0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
      0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
      0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
      0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
      0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
      0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
      0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
      0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
      0x40
      } ;

      unsigned short CRC16(unsigned char *puchMsg,unsigned short usDataLen)
      {
      unsigned char uchCRCHi = 0xff;
      unsigned char uchCRCLo = 0xff;
      unsigned int uIndex;
      while(usDataLen--)
      {
        uIndex = uchCRCLo ^ *puchMsg++;
        uchCRCLo = uchCRCHi ^ auchCRCHi[uIndex];
        uchCRCHi = auchCRCLo[uIndex];
      }
      return (uchCRCHi<<8|uchCRCLo);
      }
      void ModbusMaster_NextCmd(MBErrors ecode)
      {
      if (ecode) printf("ModbusMaster_NextCmd(%d)\n",ecode); //debug
      ModbusMaster_Idle=0; // ready to send a new command
      if (INTERROR_TIMEOUT==ecode)
      {
        if (ModbusMaster_List)
         ModbusMaster_Connected=0;
      }
      if (ModbusMaster_List)
      {
        ModbusMaster_MonitorIndex++;
        if (!ModbusMaster_MonitorList[ModbusMaster_MonitorIndex].start)
        {
         ModbusMaster_MonitorIndex=0;
         ModbusMaster_MonitorCycleTime=Time_sec()-ModbusMaster_MonitorStartTime;
         ModbusMaster_MonitorStartTime=Time_sec();
        }
      }
      else
      {
        ModbusMaster_ConnectIndex++;
        if (!ModbusMaster_ConnectList[ModbusMaster_ConnectIndex].start)
         ModbusMaster_List=1; // continue monitor list, do not restart here
      }
      if (INTERROR_TIMEOUT!=ecode&&0==ModbusMaster_Connected)
      {
        ModbusMaster_Connected=1;
        ModbusMaster_List=0;
        ModbusMaster_ConnectIndex=0;
        ModbusMaster_TallyConnections++;
        ModbusMaster_TallyCommands=0;
        ModbusMaster_TallyRetries=0;
      }
      if (!ModbusMaster_List)
        ModbusMaster_SentPtr=&ModbusMaster_ConnectList[ModbusMaster_ConnectIndex];
      else
        ModbusMaster_SentPtr=&ModbusMaster_MonitorList[ModbusMaster_MonitorIndex];
      }
      void ModbusMaster_Send(int verbose)
      {
      // send the command currently pointed to by ModbusMaster_SentPtr
      // printf("ModbusMaster_Send(%d)\n",verbose);
       
      char *chp;
      int x;
      unsigned char *xp;
      if (!ModbusMaster_List)
        printf("List:%d ConnectIndex:%d MonitorIndex:%d\n",
          ModbusMaster_List,ModbusMaster_ConnectIndex,ModbusMaster_MonitorIndex);
      if (!ModbusMaster_SentPtr->start)
      {
        printf("ModbusMaster_Send: Tried to execute at end list\n");
        ModbusMaster_NextCmd(MBERROR_NONE);
      }
       
      strncpy(ModbusMaster_packetBuild,ModbusMaster_SentPtr->start,ModbusMaster_SentPtr->len);
      chp=&ModbusMaster_packetBuild[ModbusMaster_SentPtr->len];
      switch(ModbusMaster_packetBuild[1])
      {
        case 0x10: // RW Write
         ModbusMaster_RegLoad();
         *chp++=ModbusMaster_packetBuild[5]*2;
         for (x=0;x<ModbusMaster_packetBuild[5];x++)
         {
          *chp++=(MBRegisters[x+ModbusMaster_SentPtr->reg]>>8)&0xFF;
          *chp++=MBRegisters[x+ModbusMaster_SentPtr->reg]&0xFF;
         }
         break;
        case 0x02: // Bit Read
        case 0x03: // RO Read
        case 0x04: // RW Read
         break;
        default:
         printf("Unexpected default: ModbusMaster_Send(), Function %d\n",ModbusMaster_packetBuild[1]); //debug
         break;
      }
      int csum=CRC16(ModbusMaster_packetBuild,chp-ModbusMaster_packetBuild);
      *chp++=csum&0xFF;
      *chp++=(csum>>8)&0xFF;
      if (verbose) printf("Tx:"); //debug
      for (xp=ModbusMaster_packetBuild;xp<chp;xp++)
      {
        RS232_PutChar(*xp);
        if (verbose) printf("%02x;",*xp); //debug
      }
      if (verbose) printf("\n"); //debug
       
      ModbusMaster_LastInTime=Time_sec();
      ModbusMaster_Idle=1;
      }
      MBErrors Process_Data(unsigned char *Buffer, unsigned char Count)
      {
      int regndx;
      int cnt;
      int x;
      unsigned short CRC = (((Buffer[Count-1]<<8)&0xFF00)|(Buffer[Count-2]&0xFF)); // Received CRC
      unsigned short Recalculated_CRC = CRC16(Buffer,Count-2); // Computed CRC
      if(Recalculated_CRC != CRC)
      {
        printf("Count %d\n",Count);
        printf("Checksum: Theirs:%04X Mine:%04X, %d chars\n",CRC,Recalculated_CRC,Count); //debug
        return INTERROR_CHECKSUM;
      }
      //d printf("Packet %d\n",Function);    //debug
      switch(Buffer[1])
      {
        case 0x02:
         //ModbusMaster_BitUnload();
         break;
        case 0x03:
         //printf((int)Buffer[3]);
         //printf("Buffers-",Buffer[2]);
         cnt=Buffer[2];
         //printf("Buffers-%d\n",cnt);
         //MBRegisters[2]=Buffer[4];
         MBRegisters[2]=((Buffer[3]<<8)&0xFF00)|(Buffer[4]&0x00FF);
         //printf("Register-%d\n",MBRegisters[2]);
         ModbusMaster_RegTransfer();
         break;
        case 0x04:
         regndx=ModbusMaster_SentPtr->reg;
         cnt=Buffer[2];
         //printf("Buffers-%d\n",cnt);
         //printf(cnt);
         for (x=0;x<cnt;x+=2)
          MBRegisters[regndx++]=((Buffer[3+x]<<8)&0xFF00)|(Buffer[4+x]&0x00FF);
         //printf(MBRegisters[1]);
         ModbusMaster_RegUnload();
         break;
        case 0x10:
         // no action on successful write
         break;
        default:
         printf("Unexpected default: Process_Data(), Buffer[1]=%d\n",Buffer[1]);
         break;
      }
       
      return MBERROR_NONE; //We made it to the end, return
      }
       
      void ModbusMaster_Monitor()
      {
      char c;
      if (pRS232RecIn != pRS232RecOut)
      {
        ModbusMaster_LastInTime=Time_sec();
        while (pRS232RecIn != pRS232RecOut) // data in buffer
        {
         c=RS232_GetChar();
         if (ModbusMaster_packetSize<255)
          ModbusMaster_packetBuild[ModbusMaster_packetSize++]=c;
         //d printf("%02x,",c&0xFF); //debug
        }
      }
      else
      {
        if (ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait<Time_sec() && ModbusMaster_packetSize)
        {
         int rtrn=Process_Data(ModbusMaster_packetBuild,ModbusMaster_packetSize);
         ModbusMaster_packetSize=0; // ready for next packet
         if (!rtrn)
         {
          ModbusMaster_TallyCommands++;
          ModbusMaster_NextCmd(rtrn);
          return;
         }
         printf("Error=%d, %f\n",rtrn,ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait-Time_sec()); //debug
         //d printf("\n",c); //debug
        }
        if (ModbusMaster_LastInTime+ModbusMaster_Timeout<Time_sec()) // retry test
        {
         ModbusMaster_Retry++;
         ModbusMaster_TallyRetries++;
         printf("ModbusMaster_Retry:%d\n",ModbusMaster_Retry); //debug
         if (ModbusMaster_Retry>ModbusMaster_MaxRetry)
         {
          //d printf("Failed Monitor message %d\n",ModbusMaster_MonitorIndex); //debug
          //ModbusMaster_ConnectIndex=0; // reset connection
          ModbusMaster_NextCmd(INTERROR_TIMEOUT);
         }
         else
         {
          ModbusMaster_Send(1);
         }
        }
      }
      }

      void ModbusMaster_Loop()
      {
      //d printf("ModbusMaster_Loop: ModbusMaster_Idle=%d\n",ModbusMaster_Idle); //debug
      if(!ModbusMaster_Idle)
      {
        int x;
        //if (ModbusMaster_LastInTime+ModbusMaster_EndOfPacketWait>Time_sec())
        // ;
        //printf("ModbusMaster_Loop: ModbusMaster_Idle=%d\n",ModbusMaster_Idle); //debug
        ModbusMaster_Send(0);
        //for (x=0;x<16;x++)
        //{
        // printf("%04d ",(unsigned)MBRegisters[x]);
        // if ((x&7)==7) printf("\n");
        //}
        ModbusMaster_Retry=0;
      }
      else
        ModbusMaster_Monitor();
      }
      // End MODbus
       
      int main()
      {
      ModbusMaster_Init();
      int reportsecs=10;
      double starttime;;
      double MonitorStartTime=0; // start of most recent monitor cycle
      double MonitorCycleTime=0; // seconds to call all commands in Monitor list
      int TallyConnections=0; // Number of times Connecion list has been sent
      int TallyCommands=0; // Commands since connection
      int TallyRetries=0; // Retries since connection
      starttime=Time_sec();
      TallyCommands=ModbusMaster_TallyCommands;
      for(;;) // loop forever
      {
        ModbusMaster_Loop();
        if (starttime+reportsecs<Time_sec())
        {
         printf("\nSeconds: %d\n",reportsecs);
         printf("ModbusMaster_MonitorCycleTime=%f (%f/s)\n",ModbusMaster_MonitorCycleTime,1.0/ModbusMaster_MonitorCycleTime);
         printf("ModbusMaster_TallyCommands/s=%0.lf\n",(ModbusMaster_TallyCommands-TallyCommands)/(double)reportsecs);
         printf("ModbusMaster_TallyConnections=%d\n",ModbusMaster_TallyConnections);
         printf("ModbusMaster_TallyRetries=%d\n",ModbusMaster_TallyRetries);
       
         starttime=Time_sec();
         TallyCommands=ModbusMaster_TallyCommands;
        }
      } // end of forever
          return 0;
      }
       
      Moray
       
        if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
        {
         //printf("tick\n");
         delayT = Time_sec()+tDelay;
        }
       
       

       
      On Tue, Jul 29, 2014 at 9:09 PM, engnerdan@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
       
      There was a discussion on here a while back initiated by Moray about adding some additional modbus addresses/functions to the modbus code example. I am looking to do the same thing but I am incredibly lost looking at this example program (no C experience before). If Moray or someone else could share their code and maybe help walk me through what is happening in the code that would be great.

      -Dan
       




      Group: DynoMotion Message: 9976 From: Moray Cuthill Date: 8/15/2014
      Subject: Re: Expanding on the Modbus RTU Example [2 Attachments]
      Attachments :
        Tom,
         
        if the DoMore documentation is anything like the Click, then you have to install the programming software to get the help section, which in the case of the Click doesn't really go into much beyond the basics. I actually found it pretty confusing, as it mentions lots of bits that you can ignore provided you get the basic settings right!
         
        Then to get the actual register addresses, you need to look them up in the register tables within the software itself, which in the case of the Click software is the Address Picker in the Program menu, where you then have to select the checkbox at the bottom for it to show the Modbus addresses in what format you need (Hex or 984).
         
        I actually found that simplymodus.ca site you linked to the most use in terms of explaining what i should be sending and expecting in return.
         
        Moray


        On Fri, Aug 15, 2014 at 7:04 PM, Tom Kerekes tk@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
         
        [Attachment(s) from Tom Kerekes included below]

        Hi Dan,

        I think you are probably correct regarding the no parity setting.  That message prints after the CRC (checksum) in the response string has been verified to be correct.

        I found this:


        It says that bit 8 is set in the response to indicate that there is an error.  I'm having a hard time finding the manual for the Automation Direct Modbus Software.  Were you able to find it?

        It may be that you are just trying to read/write invalid registers for your device.  I think you will need to determine which registers you want to read and change the tables to only read/write valid registers.

        HTH
        Regards
        TK


        Group: DynoMotion Message: 9987 From: daniel w Date: 8/15/2014
        Subject: Re: Expanding on the Modbus RTU Example
        Attachments :
          A little update.

          Do-More doesn't write directly to the inputs and outputs. It's a security feature. So it writes to 4 different dedicated modbus address locations. With a function code of 16 I was able to write to work location. MCH01 I think. But I wasn't able to read from input addresses or write to discrete outputs.

          So to get to this point I found the modbus addresses are different than those of the click. But now I am trying to get the inputs to be read and the discrete outputs to work.

          --- Original Message ---

          From: "Moray Cuthill moray.cuthill@... [DynoMotion]" <DynoMotion@yahoogroups.com>
          Sent: August 15, 2014 1:30 PM
          To: DynoMotion@yahoogroups.com
          Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example

           

          Tom,
           
          if the DoMore documentation is anything like the Click, then you have to install the programming software to get the help section, which in the case of the Click doesn't really go into much beyond the basics. I actually found it pretty confusing, as it mentions lots of bits that you can ignore provided you get the basic settings right!
           
          Then to get the actual register addresses, you need to look them up in the register tables within the software itself, which in the case of the Click software is the Address Picker in the Program menu, where you then have to select the checkbox at the bottom for it to show the Modbus addresses in what format you need (Hex or 984).
           
          I actually found that simplymodus.ca site you linked to the most use in terms of explaining what i should be sending and expecting in return.
           
          Moray


          On Fri, Aug 15, 2014 at 7:04 PM, Tom Kerekes tk@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
           
          [Attachment(s) from Tom Kerekes included below]

          Hi Dan,

          I think you are probably correct regarding the no parity setting.  That message prints after the CRC (checksum) in the response string has been verified to be correct.

          I found this:


          It says that bit 8 is set in the response to indicate that there is an error.  I'm having a hard time finding the manual for the Automation Direct Modbus Software.  Were you able to find it?

          It may be that you are just trying to read/write invalid registers for your device.  I think you will need to determine which registers you want to read and change the tables to only read/write valid registers.

          HTH
          Regards
          TK


          Group: DynoMotion Message: 9990 From: Moray Cuthill Date: 8/17/2014
          Subject: Re: Expanding on the Modbus RTU Example
          Attachments :
            Dan,
             
            I'm not sure how much you've read/looked into this, but I had a quick scan through the manual, and the following bit of the specs stood out-

            Modbus Client memory

            Yes, configurable up to memory limit, default 1024 input bits, 1024 coil bits, 2048

            input registers, 2048 holding registers

             

            I'm not going to install the software to find out, however it sounds to me like there should be some kind of option/configuration for mapping the accessable modbus registers to the internal registers?

            Alos, it may be worth having a search over on the AutomationDirect support forum - http://forum.automationdirect.com/
             
            Moray
             
            On Sat, Aug 16, 2014 at 3:01 AM, daniel w engnerdan@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
             

            A little update.

            Do-More doesn't write directly to the inputs and outputs. It's a security feature. So it writes to 4 different dedicated modbus address locations. With a function code of 16 I was able to write to work location. MCH01 I think. But I wasn't able to read from input addresses or write to discrete outputs.

            So to get to this point I found the modbus addresses are different than those of the click. But now I am trying to get the inputs to be read and the discrete outputs to work.

            --- Original Message ---

            From: "Moray Cuthill moray.cuthill@... [DynoMotion]" <DynoMotion@yahoogroups.com>
            Sent: August 15, 2014 1:30 PM

            To: DynoMotion@yahoogroups.com
            Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example

             

            Tom,
             
            if the DoMore documentation is anything like the Click, then you have to install the programming software to get the help section, which in the case of the Click doesn't really go into much beyond the basics. I actually found it pretty confusing, as it mentions lots of bits that you can ignore provided you get the basic settings right!
             
            Then to get the actual register addresses, you need to look them up in the register tables within the software itself, which in the case of the Click software is the Address Picker in the Program menu, where you then have to select the checkbox at the bottom for it to show the Modbus addresses in what format you need (Hex or 984).
             
            I actually found that simplymodus.ca site you linked to the most use in terms of explaining what i should be sending and expecting in return.
             
            Moray


            On Fri, Aug 15, 2014 at 7:04 PM, Tom Kerekes tk@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
             
            [Attachment(s) from Tom Kerekes included below]

            Hi Dan,

            I think you are probably correct regarding the no parity setting.  That message prints after the CRC (checksum) in the response string has been verified to be correct.

            I found this:


            It says that bit 8 is set in the response to indicate that there is an error.  I'm having a hard time finding the manual for the Automation Direct Modbus Software.  Were you able to find it?

            It may be that you are just trying to read/write invalid registers for your device.  I think you will need to determine which registers you want to read and change the tables to only read/write valid registers.

            HTH
            Regards
            TK


            Group: DynoMotion Message: 9993 From: Dan Date: 8/18/2014
            Subject: Re: Expanding on the Modbus RTU Example
            Attachments :
              Here is the info I have gathered about the D-More.
               
              This is the only string I could get to work
              {"\x01\x10\x00\x00\x00\x01", 6, 1},    // Write outputs from MBRegisters[1]
               
              Which if I understand it correctly is this
              Devise = 1
              Function code = 16 (Write Multiple registers)
              Address = 0000
              Length = 1
               
              This would take bits 56-61 and I would get a byte value depending on which ones were on. So bit 56 would give me a value of 1 in memory location MHR1 on he PLC, bit 57 would get me 2, bit 56 and 57 would give me 3, ect. But what confuses me about this is that from what I am reading the MHR section should have an address of 4XXXX
               
               
              I never got back to working with this code some more this last weekend. It took me all weekend to re-tune servos and work my way through the MPG Smooth Hardware Enc code. But my guess at this point is that because of the security/restrictions on the Do-More I will have to write all the outputs to a byte or word location and likewise read from a byte or word value location (registry). Then on the PLC side of it I need to split that byte into its 8 bits and transfer the value to the bits I am trying to read or write. Does any of that make sense?
               
               
              View album
              This album has 5 photos and will be available on SkyDrive until 11/16/2014.
              View album View album View album View album  
              Dan
               
              Sent: Sunday, August 17, 2014 5:48 AM
              Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
               
               

              Dan,
               
              I'm not sure how much you've read/looked into this, but I had a quick scan through the manual, and the following bit of the specs stood out-

              Modbus Client memory

              Yes, configurable up to memory limit, default 1024 input bits, 1024 coil bits, 2048

              input registers, 2048 holding registers

               

              I'm not going to install the software to find out, however it sounds to me like there should be some kind of option/configuration for mapping the accessable modbus registers to the internal registers?

              Alos, it may be worth having a search over on the AutomationDirect support forum - http://forum.automationdirect.com/
               
              Moray
               
              On Sat, Aug 16, 2014 at 3:01 AM, daniel w engnerdan@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
               

              A little update.

              Do-More doesn't write directly to the inputs and outputs. It's a security feature. So it writes to 4 different dedicated modbus address locations. With a function code of 16 I was able to write to work location. MCH01 I think. But I wasn't able to read from input addresses or write to discrete outputs.

              So to get to this point I found the modbus addresses are different than those of the click. But now I am trying to get the inputs to be read and the discrete outputs to work.

              --- Original Message ---

              From: "Moray Cuthill moray.cuthill@... [DynoMotion]" <DynoMotion@yahoogroups.com>
              Sent: August 15, 2014 1:30 PM

              To: DynoMotion@yahoogroups.com
              Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example

               
              Tom,
               
              if the DoMore documentation is anything like the Click, then you have to install the programming software to get the help section, which in the case of the Click doesn't really go into much beyond the basics. I actually found it pretty confusing, as it mentions lots of bits that you can ignore provided you get the basic settings right!
               
              Then to get the actual register addresses, you need to look them up in the register tables within the software itself, which in the case of the Click software is the Address Picker in the Program menu, where you then have to select the checkbox at the bottom for it to show the Modbus addresses in what format you need (Hex or 984).
               
              I actually found that simplymodus.ca site you linked to the most use in terms of explaining what i should be sending and expecting in return.
               
              Moray


              On Fri, Aug 15, 2014 at 7:04 PM, Tom Kerekes tk@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
               
              [Attachment(s) from Tom Kerekes included below]
              Hi Dan,

              I think you are probably correct regarding the no parity setting.  That message prints after the CRC (checksum) in the response string has been verified to be correct.

              I found this:


              It says that bit 8 is set in the response to indicate that there is an error.  I'm having a hard time finding the manual for the Automation Direct Modbus Software.  Were you able to find it?

              It may be that you are just trying to read/write invalid registers for your device.  I think you will need to determine which registers you want to read and change the tables to only read/write valid registers.
               
              HTH
              Regards
              TK
               
               
              Group: DynoMotion Message: 9994 From: Tom Kerekes Date: 8/18/2014
              Subject: Re: Expanding on the Modbus RTU Example
              Hi Dan,

              I don't really follow your question entirely.  But if you seem to have one register transferring to the PLC working.  I would temporarily put some recognizable value to be transferred like decimal 1234 as shown below.  Then see if you can find and see where it is in the PLC.  Later you can put Kanalog ADC values there instead and expand it to multiple values.


              // marshal and move values to be sent to PLC/Slave into MBRegisters
              void ModbusMaster_RegLoad()
              {
                  // Put some recognizable number to a register to transfer to the PLC
                  MBRegisters[1] = 1234;  
              }

              HTH
              Regards
              TK


              From: "'Dan' engnerdan@... [DynoMotion]" <DynoMotion@yahoogroups.com>
              To: DynoMotion@yahoogroups.com
              Sent: Monday, August 18, 2014 5:37 AM
              Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example

               
              Here is the info I have gathered about the D-More.
               
              This is the only string I could get to work
              {"\x01\x10\x00\x00\x00\x01", 6, 1},    // Write outputs from MBRegisters[1]
               
              Which if I understand it correctly is this
              Devise = 1
              Function code = 16 (Write Multiple registers)
              Address = 0000
              Length = 1
               
              This would take bits 56-61 and I would get a byte value depending on which ones were on. So bit 56 would give me a value of 1 in memory location MHR1 on he PLC, bit 57 would get me 2, bit 56 and 57 would give me 3, ect. But what confuses me about this is that from what I am reading the MHR section should have an address of 4XXXX
               
               
              I never got back to working with this code some more this last weekend. It took me all weekend to re-tune servos and work my way through the MPG Smooth Hardware Enc code. But my guess at this point is that because of the security/restrictions on the Do-More I will have to write all the outputs to a byte or word location and likewise read from a byte or word value location (registry). Then on the PLC side of it I need to split that byte into its 8 bits and transfer the value to the bits I am trying to read or write. Does any of that make sense?
               
               
              View album
              This album has 5 photos and will be available on SkyDrive until 11/16/2014.
              View albumView albumView albumView album 
              Dan
               
              Sent: Sunday, August 17, 2014 5:48 AM
              Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
               
               
              Dan,
               
              I'm not sure how much you've read/looked into this, but I had a quick scan through the manual, and the following bit of the specs stood out-
              Modbus Client memory
              Yes, configurable up to memory limit, default 1024 input bits, 1024 coil bits, 2048
              input registers, 2048 holding registers
               
              I'm not going to install the software to find out, however it sounds to me like there should be some kind of option/configuration for mapping the accessable modbus registers to the internal registers?

              Alos, it may be worth having a search over on the AutomationDirect support forum - http://forum.automationdirect.com/
               
              Moray
               
              On Sat, Aug 16, 2014 at 3:01 AM, daniel w engnerdan@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
               
              A little update.

              Do-More doesn't write directly to the inputs and outputs. It's a security feature. So it writes to 4 different dedicated modbus address locations. With a function code of 16 I was able to write to work location. MCH01 I think. But I wasn't able to read from input addresses or write to discrete outputs.

              So to get to this point I found the modbus addresses are different than those of the click. But now I am trying to get the inputs to be read and the discrete outputs to work.

              --- Original Message ---

              From: "Moray Cuthill moray.cuthill@... [DynoMotion]" <DynoMotion@yahoogroups.com>
              Sent: August 15, 2014 1:30 PM

              To: DynoMotion@yahoogroups.com
              Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example

               
              Tom,
               
              if the DoMore documentation is anything like the Click, then you have to install the programming software to get the help section, which in the case of the Click doesn't really go into much beyond the basics. I actually found it pretty confusing, as it mentions lots of bits that you can ignore provided you get the basic settings right!
               
              Then to get the actual register addresses, you need to look them up in the register tables within the software itself, which in the case of the Click software is the Address Picker in the Program menu, where you then have to select the checkbox at the bottom for it to show the Modbus addresses in what format you need (Hex or 984).
               
              I actually found that simplymodus.ca site you linked to the most use in terms of explaining what i should be sending and expecting in return.
               
              Moray


              On Fri, Aug 15, 2014 at 7:04 PM, Tom Kerekes tk@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
               
              [Attachment(s) from Tom Kerekes included below]
              Hi Dan,

              I think you are probably correct regarding the no parity setting.  That message prints after the CRC (checksum) in the response string has been verified to be correct.

              I found this:


              It says that bit 8 is set in the response to indicate that there is an error.  I'm having a hard time finding the manual for the Automation Direct Modbus Software.  Were you able to find it?

              It may be that you are just trying to read/write invalid registers for your device.  I think you will need to determine which registers you want to read and change the tables to only read/write valid registers.
               
              HTH
              Regards
              TK
               
               

              From: "'Dan' engnerdan@... [DynoMotion]" <DynoMotion@yahoogroups.com>
              To: DynoMotion@yahoogroups.com
              Sent: Friday, August 15, 2014 6:42 AM
              Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
               
               
              I double checked and parity is not on. I tried setting it to even, odd, off and the only time it is sending and receiving is when it is set to off. I changed every possible setting on the PLC and all that happens is the PLC stops transmitting back to the KFLOP, So my settings are correct. I have a serial communication card for this PLC with 2 additional RS232 ports, I got the same problem from the KFLOP when I tried those ports too. I am going to see if I can possibly get my hands on a Click PLC this weekend, but I think it will be next week before I can get one on loan from a previous employer.
               
              This is the PLC CPU I am trying to use.
               
              I did manage to get the beginning of message from the counsel window.
               
              Modbus failure 1
               
              Dan
               
              Sent: Thursday, August 14, 2014 4:36 PM
              Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example [1 Attachment]
               
               
              Hi Dan,

              In the received string from the device the 2nd byte (Buffer[1]) is normally expected to be either a value of 3, 4, or 16.  But it appears to be receiving a 132 and 144.  Which are probably meant to be a 4 and 16 but with 128 added (Bit7). 

              Maybe you have even parity selected on the device?

              Regards
              TK
               

              From: "'Dan' engnerdan@... [DynoMotion]" <DynoMotion@yahoogroups.com>
              To: DynoMotion@yahoogroups.com
              Sent: Thursday, August 14, 2014 12:51 PM
              Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
               
               
              Tom,
              I tried compiling and running the Modbus code as it comes with the Kmotion software (no changes) and I am getting this error on the console screen. I had nothing else running on the KFLOP at the time. The PLC was an Automation Direct Do-More configured per the notes 38400,8,1, no parity. The function codes are suppose to be the same (Modicon based Modbus RTU). Do you have any insight into what this might mean?
               
              plc error
               
              Dan
               


              Sent: Friday, August 01, 2014 9:10 AM
              Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
               
               
              Hi Dan,

              Although the Modbus code looks complicated you should only need minor changes to achieve what you describe.

              There basically two things that need to be modified:

              #1 - decide which registers need to read/and written in your Modbus device.  You will need to research your particular Modbus device to obtain this information.  Then Modify the table called ModbusMaster_MonitorList so these registers are are read and written.  This should be all that is required to magically transfer the data to/from the Modbus device and the internal MBRegister[] array within KFLOP.

              #2 - code the RegLoad() and RegUnload() functions to move data within KFLOP to/from the MBRegister Array.  In your example you would be reading the ADC() values and placing them into the MBRegister Array.

              HTH
              Regards
              TK
               

              From: "'Dan' engnerdan@... [DynoMotion]" <DynoMotion@yahoogroups.com>
              To: DynoMotion@yahoogroups.com
              Sent: Thursday, July 31, 2014 2:29 PM
              Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
               
               
              Moray,
              Thanks I will take a look at the code later. What I am trying to do is send the ADC value from 4 of the Kanalog inputs to the PLC and then have an HMI connected to the PLC display those values as moving bar graphs. These are to show the load on each of my servos and my spindle. I was going to try and modify the KmotionCNC GUI to add the bar graph load meters, but I have too much to learn with C that I don’t need to try working on C++ and the GUI too.
               
              Additionally I think it would require that I rewrite the GUI code each time there is a version update. The additional digital I/O of the PLC will not hurt any either, I am out of I/O on my Kanalog and have not even started designing my tool changer.
               
               
              Dan
               
              Sent: Tuesday, July 29, 2014 4:42 PM
              Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
               
               
              Hi,
               
              I just had to dig out a backup of my main init file to figure out how I eventually implemented things. It is some quite complicated code, as there is quite a bit of error checking/handling involved, and I can't remember exactly everything that I had to change to get it to work.
              I've copied in the relevant code from my init file, so you can compare it to the supplied modbus master example to see where I've added bits (I'd go through it and check, but I've not got the time as I'm currently in the middle of a new machine build!)
               
              For my application, I have a Click PLC with an additional IO module to control my toolchanger, and provide some additional inputs. All I'm doing via modbus, is read the current tool position from a register, write the desired position to a register, and read the current status of the inputs on the additional IO, so I'm only dealing with 3 registers.
               
              From a quick scan through, I use RegUnload() to read the additional input register to test for active bits which correspond to the inputs.
              RegLoad copies the desired tool position into a register (*TCDesired and *TCPosition are pointers to user data locations which are used for transfer to/from Mach)
              RegTransfer is used to read the current tool position from a register to the relevant userdata.
               
              What registers are actually read/written, is setup in ModbusMaster_Cmds ModbusMaster_MonitorList[]
              Each line translates to a single MBregisters[] which is then used in the RegLoad/Unload functions, which explains why I'm referencing specific array indexes within those functions.
              The key options/commands (cmd) are 3 - read input register, 4 read register (as in memory location), 10 write register (this can be any writeable register i.e. memory or output).
               
              I think that's all the basics covered. I did add a good bit debug code when doing this, so if you think there's something there that's not needed, then that's most likely why!
               
              Here's the code-
               
              // Start MODbus
              #include "RS232/ModBus/ModBusMaster.h"
              #define TCPOS 5
              #define TCDES 6

              unsigned short MBRegisters[N_MB_REGISTERS];
              double delayT = 0;
              double delayT2 = 0;
              // Constants
              int ModbusMaster_MaxRetry=3;
              double ModbusMaster_Timeout=0.5; //seconds for no response for a send command
              double ModbusMaster_ResponseTime; //seconds for last PLC response
              double ModbusMaster_CommandSentTime;
              // status and performance counters
              double ModbusMaster_MonitorStartTime=0; // start of most recent monitor cycle
              double ModbusMaster_MonitorCycleTime=0; // seconds to call all commands in Monitor list
              int ModbusMaster_TallyConnections=0; // Number of times Connection list has been sent
              int ModbusMaster_TallyCommands=0; // Commands since connection
              int ModbusMaster_TallyRetries=0; // Retries since connection
              // statuses, counters, etc
              int ModbusMaster_List=0; // 0=connect, 1=monitor
              int ModbusMaster_Connected=0;
              int ModbusMaster_MonitorIndex=0;
              int ModbusMaster_ConnectIndex=0;
              int ModbusMaster_Idle=0; // 0=idle, 1=await reply
              int ModbusMaster_Retry=0;
              double ModbusMaster_LastInTime=0;
              double ModbusMaster_EndOfPacketWait=3.5*10/9600; // wait 3.5 characters after a packet 9600=baud rate)
              char ModbusMaster_packetBuild[256]; // max length of modbus frame
              int ModbusMaster_packetSize=0;
              double *TCPosition  = (double *)&persist.UserData[(TCPOS -1)*2];
              double *TCDesired = (double *)&persist.UserData[(TCDES-1)*2];
                 
              typedef enum
              {
              MBERROR_NONE = 0,
              // Modbus codes; reported with Modbus error packet
              MBERROR_ILLEGAL_FUNCTION = 1,
              MBERROR_ILLEGAL_DATA_ADDRESS = 2, // used
              MBERROR_ILLEGAL_DATA_VALUE = 3, // used
              MBERROR_SLAVE_DEVICE_FAILURE = 4,
              MBERROR_ACKNOWLEDGE = 5,
              MBERROR_SLAVE_DEVICE_BUSY = 6,
              MBERROR_NEGATIVE_ACKNOWLEDGE = 7,
              MBERROR_MEMORY_PARITY_ERROR = 8,
              // internal codes
              INTERROR_WRONG_DEVICE = 9,
              INTERROR_CHECKSUM = 10,
              INTERROR_TIMEOUT = 11,
              } MBErrors;

              typedef struct ModbusMaster_sCmds
              {
              char *start; // has "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. Not data and Not checksum.
              int len; // length of start string. commonly 6
              int reg; // reg# is the start index into the MBRegisters array for the command
              } ModbusMaster_Cmds;
              ModbusMaster_Cmds *ModbusMaster_SentPtr; // pointer to current command. used to send, resend, interpret response.
              ModbusMaster_Cmds ModbusMaster_ConnectList[] =
              {
              // string is "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. bytelen, data, and checksum are added.
              {"\x01\x04\xF0\x00\x00\x10", 6, 2}, // Collect PLC firmware info block MBRegisters[10] for 16 registers
              {0,0,0} // end flag
              };
              ModbusMaster_Cmds ModbusMaster_MonitorList[] =
              {
              // string is "dev,cmd,adrhi,adrlo,lenhi,lenlo" bytes of modbus command. bytelen, data, and checksum are added as necessary.
              {"\x01\x04\xE0\x00\x00\x01", 6, 0}, // Read inputs to MBRegisters[0]
              {"\x01\x10\xE2\x00\x00\x01", 6, 1}, // Write outputs from MBRegisters[1]
              {"\x01\x03\x00\x00\x00\x01", 6, 2}, // Read DS1
              {"\x01\x10\x00\x01\x00\x01", 6, 3}, // Write DS2
              {0,0,0} // end flag
              };
              void ModbusMaster_Init()
              {
              printf("\nModbus Master Init\n");
              EnableRS232Cmds(RS232_BAUD_38400);
              DoRS232Cmds = FALSE;  // turn off processing RS232 input as commands
              ModbusMaster_LastInTime=Time_sec();
              ModbusMaster_EndOfPacketWait=3.5*10.0/38400; // wait 3.5 characters after a packet
              ModbusMaster_packetSize=0;
               
              ModbusMaster_Idle=0;
              ModbusMaster_SentPtr=&ModbusMaster_ConnectList[0];
              int c;
              for (c=0;c<N_MB_REGISTERS;c++)
                MBRegisters[c]=0;
               
              // make the register static arrays available to the other threads
              persist.UserData[PERSIST_MBREG_BLOCK_ADR]=(int)MBRegisters;
              //d printf("persist.UserData[%d]<=%08X\n",PERSIST_RWREG_BLOCK_ADR,MBRWRegisters); //debug
              }
              char* strncpy(char *dst,char* src,int len)
              {
              int i;
              for (i=0;i<len;i++)
                dst[i]=src[i];
              return dst;
              }
              // marshal and move values read from PLC/Slave into MBRegisters to KFlop memory
              void ModbusMaster_RegUnload()
              {
              // Move 8 PLC inputs to virtual bits via MBRegisters[0]
              // Note use SetStateBit which is Atomic and Thread Safe
               
              int i;
              if(delayT2 < Time_sec()) // anything in here only gets handled once every tDelay seconds
                {
              delayT2 = Time_sec()+2;
                //printf("Input dump%d\n", MBRegisters[0]);
              if(MBRegisters[0] & 32){
                //printf("&32Door Closed\n");
              }
              if(MBRegisters[0] & 64){
                //printf("&64Lube Low\n");
              }
              }
              //for (i=0; i<8; i++)
              // SetStateBit(48+i,(MBRegisters[0]>>i)&1);  // 8 input bits
              }
              // marshal and move values to be sent to PLC/Slave into MBRegisters
              void ModbusMaster_RegLoad()
              {
              // Move 6 virtual bits to PLC outputs via MBRegisters[1]
              MBRegisters[1] = (VirtualBits>>8)&0x3F; // the six bits after the 8 input bits
              int tcd = *TCDesired;
              MBRegisters[3] = *TCDesired;
              if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
              {
                printf("Desired-%d\n",tcd);
                delayT = Time_sec()+2;
              }
              }
              void ModbusMaster_RegTransfer()
              {
              int tc = MBRegisters[2];
              *TCPosition = tc;
              if(delayT < Time_sec()) // anything in here only gets handled once every tDelay seconds
              {
                //printf("CLICK Register-%d\n",tc);
                //delayT = Time_sec()+2;
              }
              }
              void ModbusMaster_RegWrite()
              {
              //MBRegisters[3] = persist.UserData[12];
              MBRegisters[3] = 0x06;
              }
              static unsigned char auchCRCHi[] = {
              0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
              0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
              0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
              0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
              0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
              0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
              0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
              0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
              0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
              0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
              0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
              0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
              0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
              0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
              0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
              0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
              0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
              0x40
              } ;
              static char auchCRCLo[] = {
              0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
              0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
              0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
              0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
              0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
              0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
              0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
              0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
              0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
              0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
              0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
              0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
              0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
              0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
              0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
              0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x

              (Message over 64 KB, truncated)
              Group: DynoMotion Message: 9995 From: Dan Date: 8/18/2014
              Subject: Re: Expanding on the Modbus RTU Example [7 Attachments]
              Attachments :
                I was mainly trying to share what I had learned/accomplished so far. As far as what I could get to transfer it was this.
                 
                Virtual Buts 56-61 write to register value “MHR1” on my PLC.
                 
                Virtual Bit on KFLOP        Value of MHR1 on PLC
                Virtual Bit 56 = a value of 1
                Virtual Bit 57 = a value of 2
                Virtual Bit 58 = a value of 4
                Virtual Bit 59 = a value of 8
                Virtual Bit 60 = a value of 16
                Virtual Bit 61 = a value of 32
                 
                That was using the address string “{"\x01\x10\x00\x00\x00\x01", 6, 1},    // Write outputs from MBRegisters[1]” in the c-program on the KFLOP. As I understand it, that address string breaks down to
                Device = 1
                Function code = 16 (Write Multiple registers)
                Address = 0000
                Length = 1
                 
                But from the Do-More info I have, MHR1 should be have an address of 40001. So I am not sure how I wrote to it.
                 
                Dan
                 
                Sent: Monday, August 18, 2014 12:16 PM
                Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example [7 Attachments]
                 
                 

                Hi Dan,
                 
                I don't really follow your question entirely.  But if you seem to have one register transferring to the PLC working.  I would temporarily put some recognizable value to be transferred like decimal 1234 as shown below.  Then see if you can find and see where it is in the PLC.  Later you can put Kanalog ADC values there instead and expand it to multiple values.


                // marshal and move values to be sent to PLC/Slave into MBRegisters
                void ModbusMaster_RegLoad()
                {
                    // Put some recognizable number to a register to transfer to the PLC
                    MBRegisters[1] = 1234;  
                }

                HTH
                Regards
                TK
                 
                Group: DynoMotion Message: 9996 From: Tom Kerekes Date: 8/18/2014
                Subject: Re: Expanding on the Modbus RTU Example
                Hi Dan,

                Ahh.  I'm guessing that since it isn't even possible to send an address of 0x40000 that the function code 16 inherently sets the base address to the MHR1 memory space.

                Regards,
                TK

                Group: DynoMotion Message: 10249 From: Dan Date: 10/6/2014
                Subject: Re: Expanding on the Modbus RTU Example
                Attachments :
                  Jumping back to this topic from a while back. I have the modbus communications working between my Automation Direct Do-More PLC and the KFLOP (via the KANALOG BOARD). But I have only managed to get the inputs working. I am struggling with the outputs and the original modbus master code that comes with the software (from you). The attached picture is the code as it comes from you for the section that writes the KFLOP bits to the PLC.
                   
                  modbus write
                   
                  I have tried several times to rewrite this line, but I am just not grasping what this line of code is doing.
                  MBRegisters[1] = (VirtualBits>>8)&0x3F;    // the six bits after the 8 input bits
                   
                   
                  What I want to do is write 16 extended virtual bits 1056-1071 to my MBRegisters[1]. Can you help?
                   
                   
                  Dan
                   
                  Sent: Monday, August 18, 2014 12:55 PM
                  Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example [7 Attachments]
                   
                   

                  Hi Dan,

                  Ahh.  I'm guessing that since it isn't even possible to send an address of 0x40000 that the function code 16 inherently sets the base address to the MHR1 memory space.
                   
                  Regards,
                  TK
                   
                  Group: DynoMotion Message: 10330 From: Tom Kerekes Date: 10/15/2014
                  Subject: Re: Expanding on the Modbus RTU Example
                  Hi Dan,

                  Sorry I think I somehow missed this did you ever resolve this?

                  This line:

                  MBRegisters[1] = (VirtualBits>>8)&0x3F;    // the six bits after the 8 input bits

                  takes a 32 bit word (VirtualBits) and first shifts it to the right 8 bits (so bit 8 is now to bit 0 position).  It then "masks out" the bottom 6 bits so everything at bit 6 and above is zero by performing an arithmetic "AND" operation with hex 3F.

                  IO bits 1056-1071 are in the bottom 16 bits second Extended Virtual Bits 32-bit Word.  So no shift is required, only masking 16 bits is required.

                       MBRegisters[1] = VirtualBitsEx[1] & 0xFFFF;

                  HTH
                  Regards
                  TK


                  From: "'Dan' engnerdan@... [DynoMotion]" <DynoMotion@yahoogroups.com>
                  To: DynoMotion@yahoogroups.com
                  Sent: Monday, October 6, 2014 5:50 AM
                  Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example

                   
                  Jumping back to this topic from a while back. I have the modbus communications working between my Automation Direct Do-More PLC and the KFLOP (via the KANALOG BOARD). But I have only managed to get the inputs working. I am struggling with the outputs and the original modbus master code that comes with the software (from you). The attached picture is the code as it comes from you for the section that writes the KFLOP bits to the PLC.
                   
                  modbus write
                   
                  I have tried several times to rewrite this line, but I am just not grasping what this line of code is doing.
                  MBRegisters[1] = (VirtualBits>>8)&0x3F;    // the six bits after the 8 input bits
                   
                   
                  What I want to do is write 16 extended virtual bits 1056-1071 to my MBRegisters[1]. Can you help?
                   
                   
                  Dan
                   
                  Sent: Monday, August 18, 2014 12:55 PM
                  Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example [7 Attachments]
                   
                   
                  Hi Dan,

                  Ahh.  I'm guessing that since it isn't even possible to send an address of 0x40000 that the function code 16 inherently sets the base address to the MHR1 memory space.
                   
                  Regards,
                  TK
                   

                  From: "'Dan' engnerdan@... [DynoMotion]" <DynoMotion@yahoogroups.com>
                  To: DynoMotion@yahoogroups.com
                  Sent: Monday, August 18, 2014 10:31 AM
                  Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
                   
                   
                  I was mainly trying to share what I had learned/accomplished so far. As far as what I could get to transfer it was this.
                   
                  Virtual Buts 56-61 write to register value “MHR1” on my PLC.
                   
                  Virtual Bit on KFLOP        Value of MHR1 on PLC
                  Virtual Bit 56 = a value of 1
                  Virtual Bit 57 = a value of 2
                  Virtual Bit 58 = a value of 4
                  Virtual Bit 59 = a value of 8
                  Virtual Bit 60 = a value of 16
                  Virtual Bit 61 = a value of 32
                   
                  That was using the address string “{"\x01\x10\x00\x00\x00\x01", 6, 1},    // Write outputs from MBRegisters[1]” in the c-program on the KFLOP. As I understand it, that address string breaks down to
                  Device = 1
                  Function code = 16 (Write Multiple registers)
                  Address = 0000
                  Length = 1
                   
                  But from the Do-More info I have, MHR1 should be have an address of 40001. So I am not sure how I wrote to it.
                   
                  Dan
                   


                  Sent: Monday, August 18, 2014 12:16 PM
                  Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example [7 Attachments]
                   
                   
                  Hi Dan,
                   
                  I don't really follow your question entirely.  But if you seem to have one register transferring to the PLC working.  I would temporarily put some recognizable value to be transferred like decimal 1234 as shown below.  Then see if you can find and see where it is in the PLC.  Later you can put Kanalog ADC values there instead and expand it to multiple values.


                  // marshal and move values to be sent to PLC/Slave into MBRegisters
                  void ModbusMaster_RegLoad()
                  {
                      // Put some recognizable number to a register to transfer to the PLC
                      MBRegisters[1] = 1234;  
                  }

                  HTH
                  Regards
                  TK
                   

                  From: "'Dan' engnerdan@... [DynoMotion]" <DynoMotion@yahoogroups.com>
                  To: DynoMotion@yahoogroups.com
                  Sent: Monday, August 18, 2014 5:37 AM
                  Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
                   
                   
                  Here is the info I have gathered about the D-More.
                   
                  This is the only string I could get to work
                  {"\x01\x10\x00\x00\x00\x01", 6, 1},    // Write outputs from MBRegisters[1]
                   
                  Which if I understand it correctly is this
                  Devise = 1
                  Function code = 16 (Write Multiple registers)
                  Address = 0000
                  Length = 1
                   
                  This would take bits 56-61 and I would get a byte value depending on which ones were on. So bit 56 would give me a value of 1 in memory location MHR1 on he PLC, bit 57 would get me 2, bit 56 and 57 would give me 3, ect. But what confuses me about this is that from what I am reading the MHR section should have an address of 4XXXX
                   
                   
                  I never got back to working with this code some more this last weekend. It took me all weekend to re-tune servos and work my way through the MPG Smooth Hardware Enc code. But my guess at this point is that because of the security/restrictions on the Do-More I will have to write all the outputs to a byte or word location and likewise read from a byte or word value location (registry). Then on the PLC side of it I need to split that byte into its 8 bits and transfer the value to the bits I am trying to read or write. Does any of that make sense?
                   
                   
                  View album
                  This album has 5 photos and will be available on SkyDrive until 11/16/2014.
                  View albumView albumView albumView album 
                  Dan
                   
                  Sent: Sunday, August 17, 2014 5:48 AM
                  Subject: Re: [DynoMotion] Expanding on the Modbus RTU Example
                   
                   
                  Dan,
                   
                  I'm not sure how much you've read/looked into this, but I had a quick scan through the manual, and the following bit of the specs stood out-
                  Modbus Client memory
                  Yes, configurable up to memory limit, default 1024 input bits, 1024 coil bits, 2048
                  input registers, 2048 holding registers
                   
                  I'm not going to install the software to find out, however it sounds to me like there should be some kind of option/configuration for mapping the accessable modbus registers to the internal registers?

                  Alos, it may be worth having a search over on the AutomationDirect support forum - http://forum.automationdirect.com/
                   
                  Moray
                   
                  On Sat, Aug 16, 2014 at 3:01 AM, daniel w engnerdan@... [DynoMotion] <DynoMotion@yahoogroups.com> wrote:
                   
                  A little update.

                  Do-More doesn't write directly to the inputs and outputs. It's a security feature. So it writes to 4 different dedicated modbus address locations. With a function code of 16 I was able to write to work location. MCH01 I think. But I wasn't able to read from input addresses or write to discrete outputs.

                  So to get to this point I found the modbus addresses are different than those of the click. But now I am trying to get the inputs to be read and the discrete outputs to work.

                  Group: DynoMotion Message: 10335 From: engnerdan Date: 10/16/2014
                  Subject: Re: Expanding on the Modbus RTU Example [8 Attachments]
                  Tom thanks. I had not figured that out yet. I got the shift portion but was not understanding the masking. 

                  Thabks again that will help a bunch. 


                  Sent from my Verizon Wireless 4G LTE smartphone